ARCHIVED TRAINING / apiflask/apiflask
apiflask__apiflask-632
This is a preserved training example. Membership in the current training wave has not been verified.
Problem statement
Specifying `headers` option in `@output` decorator generates invalid spec for OAS version >= 3.0.0
The OpenAPI Specification for versions 3.0.0 and higher indicates that the `Header` object must not specify either a `name` or an `in` field:
- 3.0.0: https://spec.openapis.org/oas/v3.0.0.html#header-object
- 3.1.0: https://spec.openapis.org/oas/v3.1.0.html#header-object
The current implementation introduced in #511 invokes `schema2parameters` from `apispec`, which leaves the fields in place, resulting in an invalid spec.
```python
import openapi_spec_validator
from apiflask import APIFlask, Schema
from marshmallow.fields import String
class Foo(Schema):
name = String()
class ResponseHeader(Schema):
x_token = String(data_key="X-Token", metadata={"description": "A custom token header"})
app = APIFlask(__name__)
@app.route("/foo")
@app.output(Foo, headers=ResponseHeader)
def foo():
pass
client = app.test_client()
rv = client.get("/openapi.json")
assert rv.status_code == 200
openapi_spec_validator.validate(rv.json) # Fails
```
The generated spec looks like this:
```json
{
"components": {
"schemas": {
"Foo": {
"properties": {
"name": {
"type": "string"
}
},
"type": "object"
}
}
},
"info": {
"title": "APIFlask",
"version": "0.1.0"
},
"openapi": "3.0.3",
"paths": {
"/foo": {
"get": {
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Foo"
}
}
},
"description": "Successful response",
"headers": {
"X-Token": {
"description": "A custom token header",
"in": "header",
"name": "X-Token",
"required": false,
"schema": {
"type": "string"
}
}
}
}
},
"summary": "Foo"
}
}
},
"servers": [
{
"url": "http://localhost/"
}
],
"tags": []
}
```
I considered raising this against `apispec`, since in theory it might be possible to modify `schema2parameters` to not include `name` or `in` when the `location` value is `headers`, but the `name` value is still needed to populate the key of the `headers` dictionary in the spec, so I think it makes more sense to address in this project.
Environment:
- Python version: 3.11
- Flask version: 3.1.0
- APIFlask version: 2.3.0
Training membership
opd_e4b_cispo_v5 opd_e4b_pure_v6a opd_e4b_pure_v6b opd_e4b_pure_v7 opd_e4b_pure_v7b opd_e4b_self_audit_v4
Task issues extracted from preserved verified_rft_messages training examples. This is not a verified current RL wave manifest.
Task and snapshot provenance
Snapshot captured 2026-11-12T17:06:59.476467+00:00 · Autoresearch-Arena/gemma-4-developer-agent
{
"datasets": [
"opd_e4b_cispo_v5",
"opd_e4b_pure_v6a",
"opd_e4b_pure_v6b",
"opd_e4b_pure_v7",
"opd_e4b_pure_v7b",
"opd_e4b_self_audit_v4"
],
"examples": 31,
"instance_id": "apiflask__apiflask-632",
"problem_statement": "Specifying `headers` option in `@output` decorator generates invalid spec for OAS version >= 3.0.0\nThe OpenAPI Specification for versions 3.0.0 and higher indicates that the `Header` object must not specify either a `name` or an `in` field:\r\n- 3.0.0: https://spec.openapis.org/oas/v3.0.0.html#header-object\r\n- 3.1.0: https://spec.openapis.org/oas/v3.1.0.html#header-object\r\n\r\nThe current implementation introduced in #511 invokes `schema2parameters` from `apispec`, which leaves the fields in place, resulting in an invalid spec.\r\n\r\n```python\r\nimport openapi_spec_validator\r\nfrom apiflask import APIFlask, Schema\r\nfrom marshmallow.fields import String\r\n\r\n\r\nclass Foo(Schema):\r\n name = String()\r\n\r\n\r\nclass ResponseHeader(Schema):\r\n x_token = String(data_key=\"X-Token\", metadata={\"description\": \"A custom token header\"})\r\n\r\n\r\napp = APIFlask(__name__)\r\n\r\n\r\n@app.route(\"/foo\")\r\n@app.output(Foo, headers=ResponseHeader)\r\ndef foo():\r\n pass\r\n\r\n\r\nclient = app.test_client()\r\nrv = client.get(\"/openapi.json\")\r\nassert rv.status_code == 200\r\nopenapi_spec_validator.validate(rv.json) # Fails\r\n```\r\n\r\nThe generated spec looks like this:\r\n\r\n```json\r\n{\r\n \"components\": {\r\n \"schemas\": {\r\n \"Foo\": {\r\n \"properties\": {\r\n \"name\": {\r\n \"type\": \"string\"\r\n }\r\n },\r\n \"type\": \"object\"\r\n }\r\n }\r\n },\r\n \"info\": {\r\n \"title\": \"APIFlask\",\r\n \"version\": \"0.1.0\"\r\n },\r\n \"openapi\": \"3.0.3\",\r\n \"paths\": {\r\n \"/foo\": {\r\n \"get\": {\r\n \"parameters\": [],\r\n \"responses\": {\r\n \"200\": {\r\n \"content\": {\r\n \"application/json\": {\r\n \"schema\": {\r\n \"$ref\": \"#/components/schemas/Foo\"\r\n }\r\n }\r\n },\r\n \"description\": \"Successful response\",\r\n \"headers\": {\r\n \"X-Token\": {\r\n \"description\": \"A custom token header\",\r\n \"in\": \"header\",\r\n \"name\": \"X-Token\",\r\n \"required\": false,\r\n \"schema\": {\r\n \"type\": \"string\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"summary\": \"Foo\"\r\n }\r\n }\r\n },\r\n \"servers\": [\r\n {\r\n \"url\": \"http://localhost/\"\r\n }\r\n ],\r\n \"tags\": []\r\n}\r\n```\r\n\r\nI considered raising this against `apispec`, since in theory it might be possible to modify `schema2parameters` to not include `name` or `in` when the `location` value is `headers`, but the `name` value is still needed to populate the key of the `headers` dictionary in the spec, so I think it makes more sense to address in this project.\r\n\r\n\r\nEnvironment:\r\n\r\n- Python version: 3.11\r\n- Flask version: 3.1.0\r\n- APIFlask version: 2.3.0",
"repo": "apiflask/apiflask",
"source": "v2/experiments/train_data/opd_e4b_cispo_v5/verified_rft_messages.jsonl",
"source_sha256": "a43610ad0dfb6ace95375cfae09000f41afc0338cc925af870a4ade7a9b20c4c"
}{
"captured_at": "2026-11-12T17:06:59.476467+00:00",
"membership": "archived training",
"membership_evidence": "Task issues extracted from preserved verified_rft_messages training examples. This is not a verified current RL wave manifest.",
"name": "Preserved post-training task archive",
"source": "Autoresearch-Arena/gemma-4-developer-agent",
"source_revision": "016496de222aaafca0d4ee4c18efc63ba616f76e",
"sources": [
{
"path": "v2/experiments/train_data/opd_e4b_cispo_v5/verified_rft_messages.jsonl",
"sha256": "a43610ad0dfb6ace95375cfae09000f41afc0338cc925af870a4ade7a9b20c4c"
},
{
"path": "v2/experiments/train_data/opd_e4b_hybrid_v3/verified_rft_messages.jsonl",
"sha256": "bd326303eb127e8e5e08f03daf97bb88d79d19709d59f3f93f348bc56686a8df"
},
{
"path": "v2/experiments/train_data/opd_e4b_onpolicy_v3/verified_rft_messages.jsonl",
"sha256": "960c680e797e823a0d3a22f4b1d5ef60a72971373d703b877340b0c9126aaf68"
},
{
"path": "v2/experiments/train_data/opd_e4b_pure_v6a/verified_rft_messages.jsonl",
"sha256": "f045b7ca5b3883a1e2eaee81e1c02a4a1b53dd7b7b36e98bacb2ba5823a15148"
},
{
"path": "v2/experiments/train_data/opd_e4b_pure_v6b/verified_rft_messages.jsonl",
"sha256": "0f62460a9706eea5e1605255896f191ab00cb5706c5f1f2e1c293e9a718218dc"
},
{
"path": "v2/experiments/train_data/opd_e4b_pure_v7/verified_rft_messages.jsonl",
"sha256": "e37f7bd675322f3f81500b73559d60d5f602ca0537bf19f5cb06332c728c7b72"
},
{
"path": "v2/experiments/train_data/opd_e4b_pure_v7b/verified_rft_messages.jsonl",
"sha256": "b864bdc8695183433eef6e31cb8d33fad44ba3aded2170c1ade4ad5b2944ce23"
},
{
"path": "v2/experiments/train_data/opd_e4b_self_audit_v4/verified_rft_messages.jsonl",
"sha256": "0026023745afb8f3dc1c176dc7f7171157b4ad5a66656b524af218e8e19ceee6"
},
{
"path": "v2/experiments/train_data/rft_e4b_aligned_v2/verified_rft_messages.jsonl",
"sha256": "92a4515fee9d152b0d4c510ee3ce4caaaffe2003ba7584469f8027f174aab18c"
},
{
"path": "v2/experiments/train_data/rft_v1/verified_rft_messages.jsonl",
"sha256": "a42453ccd7ac7680b0a03865e8e7080bc8bf74c8d9d193b440c441ed46f8512b"
},
{
"path": "v2/experiments/train_data/rft_v2/verified_rft_messages.jsonl",
"sha256": "18f4b3bd7e651f83c6acab479c9475e3fa50dc652a384f309e97146983ac85ba"
}
]
}