{
  "file": "F30-openai-gateway.md",
  "bullet": "Observability headers (`F30.observability-headers`).",
  "ids": [
    "F30.observability-headers"
  ],
  "line": 534,
  "script": "OID=$(control-agent-server api POST /v1/chat/completions --auth bearer --timeout 180 \\\n  --header 'X-OpenHands-Observability-Span-Name: qa_f30.span' --header 'X-OpenHands-Observability-Tags: qa-f30-a, qa-f30-b,' \\\n  --header 'X-OpenHands-Observability-Metadata: {\"qa_key\": \"qa-f30-value\", \"qa_n\": 7}' \\\n  --header 'X-OpenHands-Observability-Parent-Span-Context: qa-f30-parent' \\\n  --json '{\"model\": \"openhands_deepseek-flash\", \"messages\": [{\"role\": \"user\", \"content\": \"QA_F30_OBS: reply with one short word and use no tools.\"}]}' \\\n  --check choices.0.message.content len-ge 1 --print-header X-OpenHands-ServerConversation-ID)\ncontrol-agent-server state cat meta.json --conversation \"$OID\" --check observability_span_name eq qa_f30.span \\\n  --check observability_tags len-eq 2 --check observability_tags.1 eq qa-f30-b --check observability_metadata.qa_key eq qa-f30-value \\\n  --check observability_metadata.qa_n eq 7 --check observability_parent_span_context eq qa-f30-parent\nORID=$(control-agent-server api POST /v1/responses --auth bearer --timeout 180 --header 'X-OpenHands-Observability-Span-Name: qa_f30.responses' \\\n  --json '{\"model\": \"openhands_deepseek-flash\", \"input\": \"QA_F30_OBS_R: reply with one short word and use no tools.\"}' \\\n  --check status eq completed --print-header X-OpenHands-ServerConversation-ID)\ncontrol-agent-server state cat meta.json --conversation \"$ORID\" --check observability_span_name eq qa_f30.responses\nN0=$(control-agent-server api GET <redacted:session-cookie>/count --field .)\nBAD='{\"model\": \"openhands_qa-f30-missing\", \"messages\": [{\"role\": \"user\", \"content\": \"x\"}]}'\ncontrol-agent-server api POST /v1/chat/completions --auth bearer --header 'X-OpenHands-Observability-Span-Name: bad span' \\\n  --json \"$BAD\" --expect 422 --check detail.0.msg contains 'span name' --save F30.observability-headers/bad-span\ncontrol-agent-server api POST /v1/chat/completions --auth bearer --header 'X-OpenHands-Observability-Metadata: not-json' \\\n  --json \"$BAD\" --expect 422 --check detail eq 'X-OpenHands-Observability-Metadata must be a JSON object'\ncontrol-agent-server api POST /v1/chat/completions --auth bearer --header 'X-OpenHands-Observability-Metadata: [1]' \\\n  --json \"$BAD\" --expect 422 --check detail.0.msg contains 'must be a dictionary'\ncontrol-agent-server api POST /v1/chat/completions --auth bearer --header 'X-OpenHands-Observability-Metadata: {\"k\": [1, 1.5]}' \\\n  --json \"$BAD\" --expect 422 --check detail.0.msg contains homogeneous\ncontrol-agent-server api POST /v1/responses --auth bearer --header 'X-OpenHands-Observability-Span-Name: bad span' \\\n  --json '{\"model\": \"openhands_qa-f30-missing\", \"input\": \"x\"}' --expect 422\ncontrol-agent-server api GET <redacted:session-cookie>/count --check . eq \"$N0\" --quiet\n",
  "exit_code": 0,
  "elapsed_ms": 36114,
  "failed_line": null,
  "failed_command": null,
  "stdout": "{\n  \"ok\": true,\n  \"path\": \"meta.json\",\n  \"bytes\": 1463,\n  \"mode\": \"0o600\",\n  \"content\": {\n    \"workspace\": {\n      \"working_dir\": \"/workspace/verification-artifacts/runs/20261013-021801-0cd5-map-f30/server/workspace/project\",\n      \"kind\": \"LocalWorkspace\"\n    },\n    \"worktree\": false,\n    \"conversation_id\": null,\n    \"parent_conversation_id\": null,\n    \"confirmation_policy\": {\n      \"kind\": \"NeverConfirm\"\n    },\n    \"security_analyzer\": {\n      \"kind\": \"LLMSecurityAnalyzer\"\n    },\n    \"initial_message\": {\n      \"role\": \"user\",\n      \"content\": [\n        {\n          \"cache_prompt\": false,\n          \"type\": \"text\",\n          \"text\": \"QA_F30_OBS: reply with one short word and use no tools.\"\n        }\n      ],\n      \"run\": true\n    },\n    \"max_iterations\": 500,\n    \"stuck_detection\": true,\n    \"secrets\": {},\n    \"secrets_encrypted\": false,\n    \"tool_module_qualnames\": {\n      \"terminal\": \"openhands.tools.terminal.definition\",\n      \"file_editor\": \"openhands.tools.file_editor.definition\",\n      \"task_tracker\": \"openhands.tools.task_tracker.definition\",\n      \"browser_tool_set\": \"openhands.tools.browser_use.definition\"\n    },\n    \"client_tools\": [],\n    \"agent_launch_additions\": null,\n    \"agent_definitions\": [],\n    \"plugins\": null,\n    \"hook_config\": null,\n    \"tags\": {},\n    \"user_id\": null,\n    \"observability_metadata\": {\n      \"qa_key\": \"qa-f30-value\",\n      \"qa_n\": 7\n    },\n    \"observability_tags\": [\n      \"qa-f30-a\",\n      \"qa-f30-b\"\n    ],\n    \"observability_span_name\": \"qa_f30.span\",\n    \"observability_parent_span_context\": \"qa-f30-parent\",\n    \"autotitle\": false,\n    \"title_llm_profile\": null,\n    \"required_runtime_credential_bindings\": [],\n    \"id\": \"c50cb4b72c51407b879a4c68b8430b58\",\n    \"title\": null,\n    \"metrics\": null,\n    \"created_at\": \"2026-10-13T02:24:10.646280Z\",\n    \"updated_at\": \"2026-10-13T02:24:10.646284Z\",\n    \"forked_from_conversation_id\": null,\n    \"forked_from_event_id\": null,\n    \"launched_agent_profile\": null\n  },\n  \"checks\": [\n    {\n      \"check\": \"observability_span_name eq qa_f30.span\",\n      \"ok\": true,\n      \"actual\": \"qa_f30.span\"\n    },\n    {\n      \"check\": \"observability_tags len-eq 2\",\n      \"ok\": true,\n      \"actual\": \"[\\\"qa-f30-a\\\", \\\"qa-f30-b\\\"]\"\n    },\n    {\n      \"check\": \"observability_tags.1 eq qa-f30-b\",\n      \"ok\": true,\n      \"actual\": \"qa-f30-b\"\n    },\n    {\n      \"check\": \"observability_metadata.qa_key eq qa-f30-value\",\n      \"ok\": true,\n      \"actual\": \"qa-f30-value\"\n    },\n    {\n      \"check\": \"observability_metadata.qa_n eq 7\",\n      \"ok\": true,\n      \"actual\": \"7\"\n    },\n    {\n      \"check\": \"observability_parent_span_context eq qa-f30-parent\",\n      \"ok\": true,\n      \"actual\": \"qa-f30-parent\"\n    }\n  ]\n}\n{\n  \"ok\": true,\n  \"path\": \"meta.json\",\n  \"bytes\": 1406,\n  \"mode\": \"0o600\",\n  \"content\": {\n    \"workspace\": {\n      \"working_dir\": \"/workspace/verification-artifacts/runs/20261013-021801-0cd5-map-f30/server/workspace/project\",\n      \"kind\": \"LocalWorkspace\"\n    },\n    \"worktree\": false,\n    \"conversation_id\": null,\n    \"parent_conversation_id\": null,\n    \"confirmation_policy\": {\n      \"kind\": \"NeverConfirm\"\n    },\n    \"security_analyzer\": {\n      \"kind\": \"LLMSecurityAnalyzer\"\n    },\n    \"initial_message\": {\n      \"role\": \"user\",\n      \"content\": [\n        {\n          \"cache_prompt\": false,\n          \"type\": \"text\",\n          \"text\": \"QA_F30_OBS_R: reply with one short word and use no tools.\"\n        }\n      ],\n      \"run\": true\n    },\n    \"max_iterations\": 500,\n    \"stuck_detection\": true,\n    \"secrets\": {},\n    \"secrets_encrypted\": false,\n    \"tool_module_qualnames\": {\n      \"terminal\": \"openhands.tools.terminal.definition\",\n      \"file_editor\": \"openhands.tools.file_editor.definition\",\n      \"task_tracker\": \"openhands.tools.task_tracker.definition\",\n      \"browser_tool_set\": \"openhands.tools.browser_use.definition\"\n    },\n    \"client_tools\": [],\n    \"agent_launch_additions\": null,\n    \"agent_definitions\": [],\n    \"plugins\": null,\n    \"hook_config\": null,\n    \"tags\": {},\n    \"user_id\": null,\n    \"observability_metadata\": {},\n    \"observability_tags\": [],\n    \"observability_span_name\": \"qa_f30.responses\",\n    \"observability_parent_span_context\": null,\n    \"autotitle\": false,\n    \"title_llm_profile\": null,\n    \"required_runtime_credential_bindings\": [],\n    \"id\": \"39b478f8c0004623906b19799c4baadd\",\n    \"title\": null,\n    \"metrics\": null,\n    \"created_at\": \"2026-10-13T02:24:19.742501Z\",\n    \"updated_at\": \"2026-10-13T02:24:19.742505Z\",\n    \"forked_from_conversation_id\": null,\n    \"forked_from_event_id\": null,\n    \"launched_agent_profile\": null\n  },\n  \"checks\": [\n    {\n      \"check\": \"observability_span_name eq qa_f30.responses\",\n      \"ok\": true,\n      \"actual\": \"qa_f30.responses\"\n    }\n  ]\n}\n{\n  \"ok\": true,\n  \"status\": 422,\n  \"request\": {\n    \"method\": \"POST\",\n    \"path\": \"/v1/chat/completions\",\n    \"query\": [],\n    \"auth\": \"bearer\",\n    \"body\": {\n      \"model\": \"openhands_qa-f30-missing\",\n      \"messages\": [\n        {\n          \"role\": \"user\",\n          \"content\": \"x\"\n        }\n      ]\n    },\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 422,\n    \"headers\": {\n      \"content-length\": \"246\",\n      \"content-type\": \"application/json\"\n    },\n    \"body\": {\n      \"detail\": [\n        {\n          \"type\": \"value_error\",\n          \"loc\": [],\n          \"msg\": \"Value error, Observability span name may only contain letters, numbers, dots, underscores, colons, slashes, and hyphens\",\n          \"input\": \"bad span\",\n          \"url\": \"https://errors.pydantic.dev/2.13/v/value_error\"\n        }\n      ]\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 288,\n  \"checks\": [\n    {\n      \"check\": \"detail.0.msg contains span name\",\n      \"ok\": true,\n      \"actual\": \"Value error, Observability span name may only contain letters, numbers, dots, underscores, colons, slashes, and hyphens\"\n    }\n  ],\n  \"saved\": \"/workspace/verification-artifacts/runs/20261013-021801-0cd5-map-f30/evidence/F30.observability-headers/bad-span.json\"\n}\n{\n  \"ok\": true,\n  \"status\": 422,\n  \"request\": {\n    \"method\": \"POST\",\n    \"path\": \"/v1/chat/completions\",\n    \"query\": [],\n    \"auth\": \"bearer\",\n    \"body\": {\n      \"model\": \"openhands_qa-f30-missing\",\n      \"messages\": [\n        {\n          \"role\": \"user\",\n          \"content\": \"x\"\n        }\n      ]\n    },\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 422,\n    \"headers\": {\n      \"content-length\": \"69\",\n      \"content-type\": \"application/json\"\n    },\n    \"body\": {\n      \"detail\": \"X-OpenHands-Observability-Metadata must be a JSON object\"\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 810,\n  \"checks\": [\n    {\n      \"check\": \"detail eq X-OpenHands-Observability-Metadata must be a JSON object\",\n      \"ok\": true,\n      \"actual\": \"X-OpenHands-Observability-Metadata must be a JSON object\"\n    }\n  ]\n}\n{\n  \"ok\": true,\n  \"status\": 422,\n  \"request\": {\n    \"method\": \"POST\",\n    \"path\": \"/v1/chat/completions\",\n    \"query\": [],\n    \"auth\": \"bearer\",\n    \"body\": {\n      \"model\": \"openhands_qa-f30-missing\",\n      \"messages\": [\n        {\n          \"role\": \"user\",\n          \"content\": \"x\"\n        }\n      ]\n    },\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 422,\n    \"headers\": {\n      \"content-length\": \"176\",\n      \"content-type\": \"application/json\"\n    },\n    \"body\": {\n      \"detail\": [\n        {\n          \"type\": \"value_error\",\n          \"loc\": [],\n          \"msg\": \"Value error, Observability metadata must be a dictionary\",\n          \"input\": [\n            1\n          ],\n          \"url\": \"https://errors.pydantic.dev/2.13/v/value_error\"\n        }\n      ]\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 406,\n  \"checks\": [\n    {\n      \"check\": \"detail.0.msg contains must be a dictionary\",\n      \"ok\": true,\n      \"actual\": \"Value error, Observability metadata must be a dictionary\"\n    }\n  ]\n}\n{\n  \"ok\": true,\n  \"status\": 422,\n  \"request\": {\n    \"method\": \"POST\",\n    \"path\": \"/v1/chat/completions\",\n    \"query\": [],\n    \"auth\": \"bearer\",\n    \"body\": {\n      \"model\": \"openhands_qa-f30-missing\",\n      \"messages\": [\n        {\n          \"role\": \"user\",\n          \"content\": \"x\"\n        }\n      ]\n    },\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 422,\n    \"headers\": {\n      \"content-length\": \"338\",\n      \"content-type\": \"application/json\"\n    },\n    \"body\": {\n      \"detail\": [\n        {\n          \"type\": \"value_error\",\n          \"loc\": [],\n          \"msg\": \"Value error, Observability metadata value for 'k' must be a scalar or a homogeneous sequence of strings, booleans, integers, or floats (mixed numeric types such as [1, 1.5] are not supported by OpenTelemetry)\",\n          \"input\": {\n            \"k\": [\n              1,\n              1.5\n            ]\n          },\n          \"url\": \"https://errors.pydantic.dev/2.13/v/value_error\"\n        }\n      ]\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 388,\n  \"checks\": [\n    {\n      \"check\": \"detail.0.msg contains homogeneous\",\n      \"ok\": true,\n      \"actual\": \"Value error, Observability metadata value for 'k' must be a scalar or a homogeneous sequence of strings, booleans, integers, or floats (mixed numeric types such as [1, 1.5] are not supported by OpenTe\\u2026\"\n    }\n  ]\n}\n{\n  \"ok\": true,\n  \"status\": 422,\n  \"request\": {\n    \"method\": \"POST\",\n    \"path\": \"/v1/responses\",\n    \"query\": [],\n    \"auth\": \"bearer\",\n    \"body\": {\n      \"model\": \"openhands_qa-f30-missing\",\n      \"input\": \"x\"\n    },\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 422,\n    \"headers\": {\n      \"content-length\": \"246\",\n      \"content-type\": \"application/json\"\n    },\n    \"body\": {\n      \"detail\": [\n        {\n          \"type\": \"value_error\",\n          \"loc\": [],\n          \"msg\": \"Value error, Observability span name may only contain letters, numbers, dots, underscores, colons, slashes, and hyphens\",\n          \"input\": \"bad span\",\n          \"url\": \"https://errors.pydantic.dev/2.13/v/value_error\"\n        }\n      ]\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 376\n}\n{\n  \"ok\": true,\n  \"status\": 200,\n  \"request\": {\n    \"method\": \"GET\",\n    \"path\": \"<redacted:session-cookie>/count\",\n    \"query\": [],\n    \"auth\": \"header\",\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"content-length\": \"2\",\n      \"content-type\": \"application/json\"\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 301,\n  \"checks\": [\n    {\n      \"check\": \". eq 10\",\n      \"ok\": true,\n      \"actual\": \"10\"\n    }\n  ]\n}\n",
  "stderr": ""
}
