anthropics / anthropics/anthropic-sdk-python
Structured outputs (output_config.format/json_schema): mid-stream invalid_request_error triggered by quoted string content in the prompt, not the schema
- Lingua principale
- Python
- Stelle
- 3.9k
- Fork
- 853
- Merge medio
- 1g 18h
- PR unite (30g)
- 11
Descrizione
## Summary
Using `output_config.format` (`type: "json_schema"`) with a moderately deep schema, a request that
passes pre-flight validation and grammar compilation (HTTP `200`, headers sent, streaming begins)
fails **mid-stream** with an SSE `error` event (`invalid_request_error`, message `"Invalid request
data"`, no further detail) partway through generation — no malformed-request error is returned up
front, and the failure is content-dependent, not schema-dependent.
```json
{"type": "error", "error": {"type": "invalid_request_error", "details": null, "message": "Invalid request data"}}
```
## What triggers it
The prompt contains **ordinary double-quoted string content** (e.g. a filename in a fact record,
a Python dict-key access like `raw["payload"]` in cited source code) that the model is likely to
reference or quote back in a schema-required string field (in our case, a `citation`-style field).
Isolated by controlled substitution on a real, reliably-failing prompt+schema pair (holding
everything else byte-identical):
| Prompt variant (schema held byte-identical throughout) | Result (3 trials each) |
|---|---|
| Real prompt, unmodified — quoted content in TWO independent sections (a "context" fact record AND the cited source code) | 3/3 fail (and 8/8 in earlier larger trials) |
| Same prompt, quotes removed from the "context" section only (source code left with its `raw["payload"]` quotes) | 3/3 succeed |
| Same prompt, quotes removed from the cited source code only (context section left with its quotes) | 3/3 succeed |
**Both independent quote-bearing sections, on their own, are each enough to flip the outcome.** This
suggests the trigger probability compounds across however many double-quoted string occurrences
exist in the prompt that the model may need to reproduce/reference in its structured output, rather
than being owned by one specific construct.
**Important caveat, disclosed rather than omitted**: a drastically shortened, genericized version of
this same schema shape + a single quoted string in the prompt did **not** reproduce the failure
(6/6 succeeded). The real failing prompt is ~11KB with two independent quoted-content sections; a
short synthetic prompt with only one quoted string did not trigger it in 6 trials. So prompt length
and/or the number of independent quote occurrences both appear to matter, not just "any quote
present" — we have not fully isolated the minimal reliable trigger, only confirmed it is real,
content-driven, and NOT schema-driven (see below).
## What we ruled out
- **Not the schema.** The schema is held byte-identical across every passing and failing trial in
our testing (verified programmatically, not assumed).
- **Not `max_tokens` / output volume.** Tested at `max_tokens` from 4,000 up to the model's real
ceiling (64,000 for `claude-haiku-4-5`); real successful full responses use under 3,500 output
tokens. The failure is not a "ran out of room" case.
- **Not schema complexity limits.** We did not hit "compiled grammar is too large" or "Schema is too
complex for compilation" (the documented errors for schema-complexity problems) — our error message
is the generic `"Invalid request data"`, and removing `output_config` entirely while keeping the
same prompt succeeds every time, which also rules out anything wrong with the request outside
`output_config`.
- **Not a hard byte-level threshold.** An earlier, smaller-sample bisection (3 trials/variant) on a
different field suggested a clean deterministic byte trigger (e.g. `"aa"` succeeding while `"a"`
and `"aaa"` failed) — larger trial counts (7-8/variant) later showed this was actually a
probability gradient the small sample couldn't distinguish from determinism, not a hard rule.
## Reproduction
Self-contained script below (`anthropic_bug_repro.py`) — embeds the real failing prompt and real
schema as literals, requires only `ANTHROPIC_API_KEY` in the environment and `httpx`. Output on our
machine, run just now:
```
Real failing prompt, unmodified : ['FAIL', 'FAIL', 'FAIL']
Context-rows quotes removed only : ['ok', 'ok', 'ok']
Cited source-code quotes removed : ['ok', 'ok', 'ok']
```
## Environment
- Model: `claude-haiku-4-5`
- `anthropic-version: 2023-06-01`, `output_config.format` (GA, no beta header)
- `temperature: 0`, `stream: true`
- Transport: raw `httpx` against `https://api.anthropic.com/v1/messages` (not the Python SDK,
to rule out SDK-side request construction as a factor)
- Date observed: 2026-08-27
## Why this looks distinct from #1176 and #1185
- #1176 (`tools`/tool_use, quotes silently stripped, no error) — different code path
(`tools`/tool_use vs. our `output_config.format`/`json_schema`), and a different failure mode
(silent wrong answer vs. our hard mid-stream error).
- #1185 ("compiled grammar is too large") — a schema-complexity error with a distinct message; ours
is schema-invariant and content-triggered, with a generic, undifferentiated error message.
Happy to provide the full real prompt/schema privately if useful for debugging on your end beyond
what's in the attached script.
anthropic_bug_repro.py (click to expand)
```python
"""Self-contained repro for: Claude structured-output (output_config.format / json_schema)
mid-stream `invalid_request_error` triggered by ordinary double-quoted string content in the
prompt, when the model must reference/quote that content in a schema-required string field.
Confirmed on claude-haiku-4-5, real API, 2026-08-27. Requires ANTHROPIC_API_KEY in the environment.
"""
import json
import os
import httpx
SCHEMA_JSON = "{\"title\": \"GuideWordElicitorClaims\", \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answers\"], \"properties\": {\"answers\": {\"type\": \"array\", \"items\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"cell_id\", \"decision\", \"cause_statement_id\", \"safeguard_statement_id\", \"use_point_statement_id\"], \"properties\": {\"cell_id\": {\"type\": \"string\"}, \"decision\": {\"type\": \"string\", \"enum\": [\"answered\", \"abstain\"]}, \"cause_claim\": {\"type\": \"boolean\"}, \"citation\": {\"type\": \"string\"}, \"cause_statement_id\": {\"type\": \"string\", \"enum\": [\"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\", \"s7\", \"s8\", \"s9\", \"s10\", \"s11\", \"s12\", \"s13\", \"s14\", \"s15\", \"s16\", \"s17\", \"s18\", \"s19\", \"s20\", \"s21\", \"s22\", \"s23\", \"s24\", \"s25\", \"s26\", \"s27\", \"s28\", \"s29\", \"s30\", \"s31\", \"s32\", \"s33\", \"s34\", \"s35\", \"s36\", \"s37\", \"s38\", \"s39\", \"s40\", \"g1\", \"g2\", \"g3\", \"g4\", \"g5\", \"g6\", \"g7\", \"g8\", \"g9\", \"g10\", \"g11\", \"g12\", \"g13\", \"g14\", \"g15\", \"g16\", \"g17\", \"g18\", \"g19\", \"g20\", \"none\"]}, \"safeguard_statement_id\": {\"type\": \"string\", \"enum\": [\"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\", \"s7\", \"s8\", \"s9\", \"s10\", \"s11\", \"s12\", \"s13\", \"s14\", \"s15\", \"s16\", \"s17\", \"s18\", \"s19\", \"s20\", \"s21\", \"s22\", \"s23\", \"s24\", \"s25\", \"s26\", \"s27\", \"s28\", \"s29\", \"s30\", \"s31\", \"s32\", \"s33\", \"s34\", \"s35\", \"s36\", \"s37\", \"s38\", \"s39\", \"s40\", \"g1\", \"g2\", \"g3\", \"g4\", \"g5\", \"g6\", \"g7\", \"g8\", \"g9\", \"g10\", \"g11\", \"g12\", \"g13\", \"g14\", \"g15\", \"g16\", \"g17\", \"g18\", \"g19\", \"g20\", \"none\"]}, \"use_point_statement_id\": {\"type\": \"string\", \"enum\": [\"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\", \"s7\", \"s8\", \"s9\", \"s10\", \"s11\", \"s12\", \"s13\", \"s14\", \"s15\", \"s16\", \"s17\", \"s18\", \"s19\", \"s20\", \"s21\", \"s22\", \"s23\", \"s24\", \"s25\", \"s26\", \"s27\", \"s28\", \"s29\", \"s30\", \"s31\", \"s32\", \"s33\", \"s34\", \"s35\", \"s36\", \"s37\", \"s38\", \"s39\", \"s40\", \"g1\", \"g2\", \"g3\", \"g4\", \"g5\", \"g6\", \"g7\", \"g8\", \"g9\", \"g10\", \"g11\", \"g12\", \"g13\", \"g14\", \"g15\", \"g16\", \"g17\", \"g18\", \"g19\", \"g20\", \"none\"]}, \"consequence_grid\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"availability_resource\", \"availability_crash\", \"information_disclosure\", \"integrity_write\", \"confidentiality_read\", \"privilege_change\", \"control_flow_transfer\", \"correctness_only\"], \"properties\": {\"availability_resource\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}, \"availability_crash\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}, \"information_disclosure\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}, \"integrity_write\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}, \"confidentiality_read\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}, \"privilege_change\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}, \"control_flow_transfer\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}, \"correctness_only\": {\"type\": \"object\", \"additionalProperties\": false, \"required\": [\"answer\", \"containment\", \"residual\", \"detectability\"], \"properties\": {\"answer\": {\"type\": \"string\", \"enum\": [\"yes\", \"no\", \"unknown\"]}, \"containment\": {\"type\": \"string\", \"enum\": [\"covers\", \"covers_with_named_residual\", \"disjoint\", \"unknown\"]}, \"residual\": {\"type\": \"string\"}, \"detectability\": {\"type\": \"string\"}}}}}, \"reason\": {\"type\": \"string\"}, \"unit_summaries\": {\"type\": \"array\"}}}}}}"
SCHEMA = json.loads(SCHEMA_JSON)
PROMPT_FAILING = 'You are eliciting HAZOP-style guide-word claims. You ask; you never decide the final verdict --\na separate deterministic step computes that from your claims.\n\nYou are answering SEVERAL guide words for ONE unit and ONE parameter kind in a single call.\n\nUnit id: app.py:.read_request\nParameter kind: input_validation\n\nOpen cells -- answer every one of these, and no others. Each item names the `cell_id` you must\necho back on that item\'s answer and the `guide_word` it asks about.\n\nThere are 7 open cells. Your `answers` array must contain EXACTLY 7 elements -- one for each numbered item below, in this order, no fewer and no more. Work through the list item by item; do not stop after the first.\n\n 1. guide_word: No/Not cell_id: app.py:.read_request#input_validation#No/Not#bottom\n 2. guide_word: More/Less cell_id: app.py:.read_request#input_validation#More/Less#bottom\n 3. guide_word: As well as cell_id: app.py:.read_request#input_validation#As well as#bottom\n 4. guide_word: Part of cell_id: app.py:.read_request#input_validation#Part of#bottom\n 5. guide_word: Reverse cell_id: app.py:.read_request#input_validation#Reverse#bottom\n 6. guide_word: Other than cell_id: app.py:.read_request#input_validation#Other than#bottom\n 7. guide_word: Early/Late cell_id: app.py:.read_request#input_validation#Early/Late#bottom\n\nNever construct or modify a `cell_id` yourself -- always echo back, unchanged, the exact\n`cell_id` you were given alongside the guide word you are answering.\n\nContext rows (facts already established about this unit; comments are excluded from this view by\ndefault):\n\n[%{index: "r4", row: %{file: "app.py", origin: "authored", generated: false}}]\n\nSource code for this unit, at lines 23-24 (comments have already been removed from\nthis view -- their ABSENCE does not mean the real code has none, and you must not reason about a\ncomment you cannot see; base every answer only on the code shown below):\n\n```\ndef read_request(raw):\n return raw["payload"]\n```\n\nYou are always given a real body above when you are asked to answer. If the block above is ever\ntruly empty, that is not "an empty function" -- it means no source could be shown to you, and the\nonly honest answer for every open cell in this bundle is `decision: "abstain"` with a reason\nsaying no source was available; do not guess about code you cannot see.\n\nMECHANICAL FACTS ABOUT THIS FUNCTION, READ OUT OF ITS CODE PROPERTY GRAPH\n\nEverything in the three lists below was computed from the graph, not written by a person and not\ninferred from the text. Four rules govern how to read them, and each one is here because ignoring\nit produced a confident, wrong answer in testing:\n\n1. A statement\'s "normalised rendering" is NOT source text. It is the graph\'s own normalised form,\n produced by the very desugaring that makes these facts computable, and only about four in ten of\n them appear verbatim in the file. NEVER expect a normalised rendering and its source line to\n match, and never treat a mismatch as evidence about the code. Anchor on the id and the line\n number; read the source line for what the programmer actually wrote.\n2. "We found none" is a statement about OUR extraction, never proof that the code has no such\n thing. An empty fact list is a gap on our side until something else rules that out.\n3. Only some of these facts are guards. A repetition fact, an arm-selection fact, an exception-path\n fact and a loop-carried dependence are each labelled as what they are, and none of them is a\n security check. Do not promote one into a safeguard.\n4. Whether a real guard ADDRESSES a particular consequence is still your judgment, and it is the\n one thing these facts deliberately do not answer. They tell you which side of `is_sanitised` a\n statement runs on; they do not tell you whether `is_sanitised` sanitises anything.\n\nGuard conditions you may cite, one per `gN` id. Citing a `gN` is the right answer when the check\nitself is the problem -- it exists, but it is too weak, or it checks the wrong thing:\n\nThe guard list for this cell is EMPTY: we found no control-structure condition in it. That is a statement about OUR extraction, not proof the code is unguarded.\n\nStatements you may cite, one per `sN` id, each followed by the containment facts we could compute\nfor it:\n\ns1 | line unknown to us | None\n normalised rendering (NOT source text): return raw["payload"]\n source line: not shown -- this statement lies outside the source slice for this unit (it was merged in from a nested function). That is a gap in our extraction.\n - No containment fact was extracted for this statement. That means WE FOUND NONE, which is not the same as \'nothing controls it\' -- see the caveat list above.\n\nCell-level facts:\n\nNo cell-level facts (decorators, declared parameter types, recursion, coverage gaps) were recorded for this cell.\n\nFor EACH open cell listed above, answer for its guide word, in this order:\n\n1. **Cause claim.** Does a cause for the deviation that guide word asks about actually exist IN\n THE CODE shown above? The question is whether a cause exists in the code, never whether an\n engineer would plausibly introduce it, how likely it is, or how it might have arisen -- v3\'s own\n evidence tiering tags the claim that this precise wording distinction is what converts a\n real-world case (ScreenOS) from a coin-flip to a reliable catch as `[R]`: reasoning from case\n studies, not a controlled measurement on this pipeline. It may motivate this wording; it does\n not prove it works here. Answer the question as asked regardless.\n\n2. **Three anchors, picked from the id lists above -- required on every answer.** These are how the\n deterministic step locates what you are talking about, so they are ids, never prose:\n - `cause_statement_id`: the `sN` statement (or `gN` condition) that IS the cause you just\n claimed. `"none"` when you claimed no cause.\n - `safeguard_statement_id`: the `sN`/`gN` that stops the deviation from mattering, if one is\n present in the lists above. `"none"` when nothing in this function addresses it -- and\n `"none"` is a real, useful answer, not a failure to find something.\n - `use_point_statement_id`: the `sN` where the deviated value is actually USED -- where it\n reaches the sink, the interpreter, the write. `"none"` when there is no such statement here.\n Pick only ids that appear in THIS cell\'s two lists above. Do not invent an id, do not renumber,\n and do not use an id from another cell. If the right thing to point at is not in either list,\n answer `"none"` and say why in `citation`.\n\n3. **`citation`.** One short phrase, in your own words, saying where you are looking and why. This\n is read by a human; nothing checks its format, and no format is required of it. It never\n substitutes for the three ids above.\n\n4. **If, and only if, a cause exists**, answer for EACH of these eight fixed consequence classes\n -- `availability_resource`, `availability_crash`, `information_disclosure`, `integrity_write`,\n `confidentiality_read`, `privilege_change`, `control_flow_transfer`, `correctness_only` --\n whether this cause could lead to that consequence: `yes`, `no`, or `unknown`. For every `yes`,\n also answer:\n - containment: does an existing safeguard fully `cover` it, `cover` it but leave a\n `covers_with_named_residual` gap you must name, leave it `disjoint` (no safeguard applies at\n all), or is containment itself `unknown`?\n - residual (only if `covers_with_named_residual`): name the specific uncovered dimension.\n - detectability: would an exploit be `externally_observable`, only `logged_internally`, or\n `silent`?\n\n5. **Unit summaries, always -- whether or not a cause exists.** For each code unit (function /\n method) visible in the context rows, report a small structured summary: its identifier, and\n whether the unit itself *interprets* an argument handed to it -- that is, resolves it as SQL, a\n shell command, a template, a filesystem/URL path, a serialized object, or a directory/naming\n lookup that can load or fetch something. Judge only the unit\'s OWN body: a unit that merely\n passes an argument to another unit is not itself interpreting, and saying so is not a mistake\n to be avoided -- the propagation step works that out for itself and needs the un-inflated\n answer to do it. Do NOT report which units call which -- that is read mechanically from the\n real code graph, not from what you can see in these context rows.\n\n This is the one channel by which a sink nobody has registered yet can be discovered, so the\n bar is what the code shows, not whether the sink is famous.\n\nIf you cannot in good conscience claim a cause exists or does not, for one of the open cells --\nthat guide word is genuinely ambiguous from the context shown -- decline just that one cell\ninstead of guessing; answer the others normally.\n\nRespond with a single JSON object: `{"answers": [...]}`.\n\nBEFORE YOU FINISH: go back to the numbered "Open cells" list near the top and check you have\nproduced one element for EVERY numbered item, in that order. One element per item -- no fewer, no\nmore, no repeats. An answer set that covers only some of the numbered items is rejected whole and\nthe work is wasted, so finishing the list matters more than length: if you are running long, make\nthe later answers terser rather than dropping them.\n\nEach element is shaped one of these two ways. `cause_statement_id`, `safeguard_statement_id` and\n`use_point_statement_id` are required on EVERY element, including a decline; use `"none"` where no\nid applies.\n\nBoth `answered` shapes may carry `unit_summaries`, a list of `{"unit_id": "",\n"is_interpreting": true|false}` -- no `calls` field; adjacency is supplied mechanically, not by\nyou. Omit the key entirely if the context rows show no code unit you can summarise; do not emit\nplaceholder rows.\n\nNo cause: `{"cell_id": "", "decision": "answered", "cause_claim":\nfalse, "citation": "", "cause_statement_id": "none", "safeguard_statement_id": "none",\n"use_point_statement_id": "none", "unit_summaries": [...]}`\n\nCause found: `{"cell_id": "", "decision": "answered", "cause_claim":\ntrue, "citation": "", "cause_statement_id": "",\n"safeguard_statement_id": "", "use_point_statement_id": "",\n"consequence_grid":\n{"availability_resource": {"answer": "yes|no|unknown", "containment":\n"covers|covers_with_named_residual|disjoint|unknown|null", "residual": "",\n"detectability": "externally_observable|logged_internally|silent or null"}, "availability_crash":\n{...}, "information_disclosure": {...}, "integrity_write": {...}, "confidentiality_read":\n{...}, "privilege_change": {...}, "control_flow_transfer": {...}, "correctness_only":\n{...}}}\n\nDecline: `{"cell_id": "", "decision": "abstain", "reason": "", "cause_statement_id": "none",\n"safeguard_statement_id": "none", "use_point_statement_id": "none"}`\n\nAll eight classes are required whenever `cause_claim` is `true`. `unit_summaries` is optional on\neither `answered` shape. `cell_id` is required on every element of `answers`. No other keys, no\nprose outside the JSON object.\n'
# Same prompt, ONLY the context-rows section's quoted values replaced with an
# unquoted-equivalent shape -- the cited source code's own quotes (`raw["payload"]`)
# are left untouched.
PROMPT_CONTEXT_UNQUOTED = 'You are eliciting HAZOP-style guide-word claims. You ask; you never decide the final verdict --\na separate deterministic step computes that from your claims.\n\nYou are answering SEVERAL guide words for ONE unit and ONE parameter kind in a single call.\n\nUnit id: app.py:.read_request\nParameter kind: input_validation\n\nOpen cells -- answer every one of these, and no others. Each item names the `cell_id` you must\necho back on that item\'s answer and the `guide_word` it asks about.\n\nThere are 7 open cells. Your `answers` array must contain EXACTLY 7 elements -- one for each numbered item below, in this order, no fewer and no more. Work through the list item by item; do not stop after the first.\n\n 1. guide_word: No/Not cell_id: app.py:.read_request#input_validation#No/Not#bottom\n 2. guide_word: More/Less cell_id: app.py:.read_request#input_validation#More/Less#bottom\n 3. guide_word: As well as cell_id: app.py:.read_request#input_validation#As well as#bottom\n 4. guide_word: Part of cell_id: app.py:.read_request#input_validation#Part of#bottom\n 5. guide_word: Reverse cell_id: app.py:.read_request#input_validation#Reverse#bottom\n 6. guide_word: Other than cell_id: app.py:.read_request#input_validation#Other than#bottom\n 7. guide_word: Early/Late cell_id: app.py:.read_request#input_validation#Early/Late#bottom\n\nNever construct or modify a `cell_id` yourself -- always echo back, unchanged, the exact\n`cell_id` you were given alongside the guide word you are answering.\n\nContext rows (facts already established about this unit; comments are excluded from this view by\ndefault):\n\n[]\n\nSource code for this unit, at lines 23-24 (comments have already been removed from\nthis view -- their ABSENCE does not mean the real code has none, and you must not reason about a\ncomment you cannot see; base every answer only on the code shown below):\n\n```\ndef read_request(raw):\n return raw["payload"]\n```\n\nYou are always given a real body above when you are asked to answer. If the block above is ever\ntruly empty, that is not "an empty function" -- it means no source could be shown to you, and the\nonly honest answer for every open cell in this bundle is `decision: "abstain"` with a reason\nsaying no source was available; do not guess about code you cannot see.\n\nMECHANICAL FACTS ABOUT THIS FUNCTION, READ OUT OF ITS CODE PROPERTY GRAPH\n\nEverything in the three lists below was computed from the graph, not written by a person and not\ninferred from the text. Four rules govern how to read them, and each one is here because ignoring\nit produced a confident, wrong answer in testing:\n\n1. A statement\'s "normalised rendering" is NOT source text. It is the graph\'s own normalised form,\n produced by the very desugaring that makes these facts computable, and only about four in ten of\n them appear verbatim in the file. NEVER expect a normalised rendering and its source line to\n match, and never treat a mismatch as evidence about the code. Anchor on the id and the line\n number; read the source line for what the programmer actually wrote.\n2. "We found none" is a statement about OUR extraction, never proof that the code has no such\n thing. An empty fact list is a gap on our side until something else rules that out.\n3. Only some of these facts are guards. A repetition fact, an arm-selection fact, an exception-path\n fact and a loop-carried dependence are each labelled as what they are, and none of them is a\n security check. Do not promote one into a safeguard.\n4. Whether a real guard ADDRESSES a particular consequence is still your judgment, and it is the\n one thing these facts deliberately do not answer. They tell you which side of `is_sanitised` a\n statement runs on; they do not tell you whether `is_sanitised` sanitises anything.\n\nGuard conditions you may cite, one per `gN` id. Citing a `gN` is the right answer when the check\nitself is the problem -- it exists, but it is too weak, or it checks the wrong thing:\n\nThe guard list for this cell is EMPTY: we found no control-structure condition in it. That is a statement about OUR extraction, not proof the code is unguarded.\n\nStatements you may cite, one per `sN` id, each followed by the containment facts we could compute\nfor it:\n\ns1 | line unknown to us | None\n normalised rendering (NOT source text): return raw["payload"]\n source line: not shown -- this statement lies outside the source slice for this unit (it was merged in from a nested function). That is a gap in our extraction.\n - No containment fact was extracted for this statement. That means WE FOUND NONE, which is not the same as \'nothing controls it\' -- see the caveat list above.\n\nCell-level facts:\n\nNo cell-level facts (decorators, declared parameter types, recursion, coverage gaps) were recorded for this cell.\n\nFor EACH open cell listed above, answer for its guide word, in this order:\n\n1. **Cause claim.** Does a cause for the deviation that guide word asks about actually exist IN\n THE CODE shown above? The question is whether a cause exists in the code, never whether an\n engineer would plausibly introduce it, how likely it is, or how it might have arisen -- v3\'s own\n evidence tiering tags the claim that this precise wording distinction is what converts a\n real-world case (ScreenOS) from a coin-flip to a reliable catch as `[R]`: reasoning from case\n studies, not a controlled measurement on this pipeline. It may motivate this wording; it does\n not prove it works here. Answer the question as asked regardless.\n\n2. **Three anchors, picked from the id lists above -- required on every answer.** These are how the\n deterministic step locates what you are talking about, so they are ids, never prose:\n - `cause_statement_id`: the `sN` statement (or `gN` condition) that IS the cause you just\n claimed. `"none"` when you claimed no cause.\n - `safeguard_statement_id`: the `sN`/`gN` that stops the deviation from mattering, if one is\n present in the lists above. `"none"` when nothing in this function addresses it -- and\n `"none"` is a real, useful answer, not a failure to find something.\n - `use_point_statement_id`: the `sN` where the deviated value is actually USED -- where it\n reaches the sink, the interpreter, the write. `"none"` when there is no such statement here.\n Pick only ids that appear in THIS cell\'s two lists above. Do not invent an id, do not renumber,\n and do not use an id from another cell. If the right thing to point at is not in either list,\n answer `"none"` and say why in `citation`.\n\n3. **`citation`.** One short phrase, in your own words, saying where you are looking and why. This\n is read by a human; nothing checks its format, and no format is required of it. It never\n substitutes for the three ids above.\n\n4. **If, and only if, a cause exists**, answer for EACH of these eight fixed consequence classes\n -- `availability_resource`, `availability_crash`, `information_disclosure`, `integrity_write`,\n `confidentiality_read`, `privilege_change`, `control_flow_transfer`, `correctness_only` --\n whether this cause could lead to that consequence: `yes`, `no`, or `unknown`. For every `yes`,\n also answer:\n - containment: does an existing safeguard fully `cover` it, `cover` it but leave a\n `covers_with_named_residual` gap you must name, leave it `disjoint` (no safeguard applies at\n all), or is containment itself `unknown`?\n - residual (only if `covers_with_named_residual`): name the specific uncovered dimension.\n - detectability: would an exploit be `externally_observable`, only `logged_internally`, or\n `silent`?\n\n5. **Unit summaries, always -- whether or not a cause exists.** For each code unit (function /\n method) visible in the context rows, report a small structured summary: its identifier, and\n whether the unit itself *interprets* an argument handed to it -- that is, resolves it as SQL, a\n shell command, a template, a filesystem/URL path, a serialized object, or a directory/naming\n lookup that can load or fetch something. Judge only the unit\'s OWN body: a unit that merely\n passes an argument to another unit is not itself interpreting, and saying so is not a mistake\n to be avoided -- the propagation step works that out for itself and needs the un-inflated\n answer to do it. Do NOT report which units call which -- that is read mechanically from the\n real code graph, not from what you can see in these context rows.\n\n This is the one channel by which a sink nobody has registered yet can be discovered, so the\n bar is what the code shows, not whether the sink is famous.\n\nIf you cannot in good conscience claim a cause exists or does not, for one of the open cells --\nthat guide word is genuinely ambiguous from the context shown -- decline just that one cell\ninstead of guessing; answer the others normally.\n\nRespond with a single JSON object: `{"answers": [...]}`.\n\nBEFORE YOU FINISH: go back to the numbered "Open cells" list near the top and check you have\nproduced one element for EVERY numbered item, in that order. One element per item -- no fewer, no\nmore, no repeats. An answer set that covers only some of the numbered items is rejected whole and\nthe work is wasted, so finishing the list matters more than length: if you are running long, make\nthe later answers terser rather than dropping them.\n\nEach element is shaped one of these two ways. `cause_statement_id`, `safeguard_statement_id` and\n`use_point_statement_id` are required on EVERY element, including a decline; use `"none"` where no\nid applies.\n\nBoth `answered` shapes may carry `unit_summaries`, a list of `{"unit_id": "",\n"is_interpreting": true|false}` -- no `calls` field; adjacency is supplied mechanically, not by\nyou. Omit the key entirely if the context rows show no code unit you can summarise; do not emit\nplaceholder rows.\n\nNo cause: `{"cell_id": "", "decision": "answered", "cause_claim":\nfalse, "citation": "", "cause_statement_id": "none", "safeguard_statement_id": "none",\n"use_point_statement_id": "none", "unit_summaries": [...]}`\n\nCause found: `{"cell_id": "", "decision": "answered", "cause_claim":\ntrue, "citation": "", "cause_statement_id": "",\n"safeguard_statement_id": "", "use_point_statement_id": "",\n"consequence_grid":\n{"availability_resource": {"answer": "yes|no|unknown", "containment":\n"covers|covers_with_named_residual|disjoint|unknown|null", "residual": "",\n"detectability": "externally_observable|logged_internally|silent or null"}, "availability_crash":\n{...}, "information_disclosure": {...}, "integrity_write": {...}, "confidentiality_read":\n{...}, "privilege_change": {...}, "control_flow_transfer": {...}, "correctness_only":\n{...}}}\n\nDecline: `{"cell_id": "", "decision": "abstain", "reason": "", "cause_statement_id": "none",\n"safeguard_statement_id": "none", "use_point_statement_id": "none"}`\n\nAll eight classes are required whenever `cause_claim` is `true`. `unit_summaries` is optional on\neither `answered` shape. `cell_id` is required on every element of `answers`. No other keys, no\nprose outside the JSON object.\n'
# Same original prompt, ONLY the cited source code's quotes removed
# (`raw["payload"]` -> `raw[payload]`) -- the context-rows quoted values are left untouched.
PROMPT_SOURCE_UNQUOTED = 'You are eliciting HAZOP-style guide-word claims. You ask; you never decide the final verdict --\na separate deterministic step computes that from your claims.\n\nYou are answering SEVERAL guide words for ONE unit and ONE parameter kind in a single call.\n\nUnit id: app.py:.read_request\nParameter kind: input_validation\n\nOpen cells -- answer every one of these, and no others. Each item names the `cell_id` you must\necho back on that item\'s answer and the `guide_word` it asks about.\n\nThere are 7 open cells. Your `answers` array must contain EXACTLY 7 elements -- one for each numbered item below, in this order, no fewer and no more. Work through the list item by item; do not stop after the first.\n\n 1. guide_word: No/Not cell_id: app.py:.read_request#input_validation#No/Not#bottom\n 2. guide_word: More/Less cell_id: app.py:.read_request#input_validation#More/Less#bottom\n 3. guide_word: As well as cell_id: app.py:.read_request#input_validation#As well as#bottom\n 4. guide_word: Part of cell_id: app.py:.read_request#input_validation#Part of#bottom\n 5. guide_word: Reverse cell_id: app.py:.read_request#input_validation#Reverse#bottom\n 6. guide_word: Other than cell_id: app.py:.read_request#input_validation#Other than#bottom\n 7. guide_word: Early/Late cell_id: app.py:.read_request#input_validation#Early/Late#bottom\n\nNever construct or modify a `cell_id` yourself -- always echo back, unchanged, the exact\n`cell_id` you were given alongside the guide word you are answering.\n\nContext rows (facts already established about this unit; comments are excluded from this view by\ndefault):\n\n[%{index: "r4", row: %{file: "app.py", origin: "authored", generated: false}}]\n\nSource code for this unit, at lines 23-24 (comments have already been removed from\nthis view -- their ABSENCE does not mean the real code has none, and you must not reason about a\ncomment you cannot see; base every answer only on the code shown below):\n\n```\ndef read_request(raw):\n return raw[payload]\n```\n\nYou are always given a real body above when you are asked to answer. If the block above is ever\ntruly empty, that is not "an empty function" -- it means no source could be shown to you, and the\nonly honest answer for every open cell in this bundle is `decision: "abstain"` with a reason\nsaying no source was available; do not guess about code you cannot see.\n\nMECHANICAL FACTS ABOUT THIS FUNCTION, READ OUT OF ITS CODE PROPERTY GRAPH\n\nEverything in the three lists below was computed from the graph, not written by a person and not\ninferred from the text. Four rules govern how to read them, and each one is here because ignoring\nit produced a confident, wrong answer in testing:\n\n1. A statement\'s "normalised rendering" is NOT source text. It is the graph\'s own normalised form,\n produced by the very desugaring that makes these facts computable, and only about four in ten of\n them appear verbatim in the file. NEVER expect a normalised rendering and its source line to\n match, and never treat a mismatch as evidence about the code. Anchor on the id and the line\n number; read the source line for what the programmer actually wrote.\n2. "We found none" is a statement about OUR extraction, never proof that the code has no such\n thing. An empty fact list is a gap on our side until something else rules that out.\n3. Only some of these facts are guards. A repetition fact, an arm-selection fact, an exception-path\n fact and a loop-carried dependence are each labelled as what they are, and none of them is a\n security check. Do not promote one into a safeguard.\n4. Whether a real guard ADDRESSES a particular consequence is still your judgment, and it is the\n one thing these facts deliberately do not answer. They tell you which side of `is_sanitised` a\n statement runs on; they do not tell you whether `is_sanitised` sanitises anything.\n\nGuard conditions you may cite, one per `gN` id. Citing a `gN` is the right answer when the check\nitself is the problem -- it exists, but it is too weak, or it checks the wrong thing:\n\nThe guard list for this cell is EMPTY: we found no control-structure condition in it. That is a statement about OUR extraction, not proof the code is unguarded.\n\nStatements you may cite, one per `sN` id, each followed by the containment facts we could compute\nfor it:\n\ns1 | line unknown to us | None\n normalised rendering (NOT source text): return raw[payload]\n source line: not shown -- this statement lies outside the source slice for this unit (it was merged in from a nested function). That is a gap in our extraction.\n - No containment fact was extracted for this statement. That means WE FOUND NONE, which is not the same as \'nothing controls it\' -- see the caveat list above.\n\nCell-level facts:\n\nNo cell-level facts (decorators, declared parameter types, recursion, coverage gaps) were recorded for this cell.\n\nFor EACH open cell listed above, answer for its guide word, in this order:\n\n1. **Cause claim.** Does a cause for the deviation that guide word asks about actually exist IN\n THE CODE shown above? The question is whether a cause exists in the code, never whether an\n engineer would plausibly introduce it, how likely it is, or how it might have arisen -- v3\'s own\n evidence tiering tags the claim that this precise wording distinction is what converts a\n real-world case (ScreenOS) from a coin-flip to a reliable catch as `[R]`: reasoning from case\n studies, not a controlled measurement on this pipeline. It may motivate this wording; it does\n not prove it works here. Answer the question as asked regardless.\n\n2. **Three anchors, picked from the id lists above -- required on every answer.** These are how the\n deterministic step locates what you are talking about, so they are ids, never prose:\n - `cause_statement_id`: the `sN` statement (or `gN` condition) that IS the cause you just\n claimed. `"none"` when you claimed no cause.\n - `safeguard_statement_id`: the `sN`/`gN` that stops the deviation from mattering, if one is\n present in the lists above. `"none"` when nothing in this function addresses it -- and\n `"none"` is a real, useful answer, not a failure to find something.\n - `use_point_statement_id`: the `sN` where the deviated value is actually USED -- where it\n reaches the sink, the interpreter, the write. `"none"` when there is no such statement here.\n Pick only ids that appear in THIS cell\'s two lists above. Do not invent an id, do not renumber,\n and do not use an id from another cell. If the right thing to point at is not in either list,\n answer `"none"` and say why in `citation`.\n\n3. **`citation`.** One short phrase, in your own words, saying where you are looking and why. This\n is read by a human; nothing checks its format, and no format is required of it. It never\n substitutes for the three ids above.\n\n4. **If, and only if, a cause exists**, answer for EACH of these eight fixed consequence classes\n -- `availability_resource`, `availability_crash`, `information_disclosure`, `integrity_write`,\n `confidentiality_read`, `privilege_change`, `control_flow_transfer`, `correctness_only` --\n whether this cause could lead to that consequence: `yes`, `no`, or `unknown`. For every `yes`,\n also answer:\n - containment: does an existing safeguard fully `cover` it, `cover` it but leave a\n `covers_with_named_residual` gap you must name, leave it `disjoint` (no safeguard applies at\n all), or is containment itself `unknown`?\n - residual (only if `covers_with_named_residual`): name the specific uncovered dimension.\n - detectability: would an exploit be `externally_observable`, only `logged_internally`, or\n `silent`?\n\n5. **Unit summaries, always -- whether or not a cause exists.** For each code unit (function /\n method) visible in the context rows, report a small structured summary: its identifier, and\n whether the unit itself *interprets* an argument handed to it -- that is, resolves it as SQL, a\n shell command, a template, a filesystem/URL path, a serialized object, or a directory/naming\n lookup that can load or fetch something. Judge only the unit\'s OWN body: a unit that merely\n passes an argument to another unit is not itself interpreting, and saying so is not a mistake\n to be avoided -- the propagation step works that out for itself and needs the un-inflated\n answer to do it. Do NOT report which units call which -- that is read mechanically from the\n real code graph, not from what you can see in these context rows.\n\n This is the one channel by which a sink nobody has registered yet can be discovered, so the\n bar is what the code shows, not whether the sink is famous.\n\nIf you cannot in good conscience claim a cause exists or does not, for one of the open cells --\nthat guide word is genuinely ambiguous from the context shown -- decline just that one cell\ninstead of guessing; answer the others normally.\n\nRespond with a single JSON object: `{"answers": [...]}`.\n\nBEFORE YOU FINISH: go back to the numbered "Open cells" list near the top and check you have\nproduced one element for EVERY numbered item, in that order. One element per item -- no fewer, no\nmore, no repeats. An answer set that covers only some of the numbered items is rejected whole and\nthe work is wasted, so finishing the list matters more than length: if you are running long, make\nthe later answers terser rather than dropping them.\n\nEach element is shaped one of these two ways. `cause_statement_id`, `safeguard_statement_id` and\n`use_point_statement_id` are required on EVERY element, including a decline; use `"none"` where no\nid applies.\n\nBoth `answered` shapes may carry `unit_summaries`, a list of `{"unit_id": "",\n"is_interpreting": true|false}` -- no `calls` field; adjacency is supplied mechanically, not by\nyou. Omit the key entirely if the context rows show no code unit you can summarise; do not emit\nplaceholder rows.\n\nNo cause: `{"cell_id": "", "decision": "answered", "cause_claim":\nfalse, "citation": "", "cause_statement_id": "none", "safeguard_statement_id": "none",\n"use_point_statement_id": "none", "unit_summaries": [...]}`\n\nCause found: `{"cell_id": "", "decision": "answered", "cause_claim":\ntrue, "citation": "", "cause_statement_id": "",\n"safeguard_statement_id": "", "use_point_statement_id": "",\n"consequence_grid":\n{"availability_resource": {"answer": "yes|no|unknown", "containment":\n"covers|covers_with_named_residual|disjoint|unknown|null", "residual": "",\n"detectability": "externally_observable|logged_internally|silent or null"}, "availability_crash":\n{...}, "information_disclosure": {...}, "integrity_write": {...}, "confidentiality_read":\n{...}, "privilege_change": {...}, "control_flow_transfer": {...}, "correctness_only":\n{...}}}\n\nDecline: `{"cell_id": "", "decision": "abstain", "reason": "", "cause_statement_id": "none",\n"safeguard_statement_id": "none", "use_point_statement_id": "none"}`\n\nAll eight classes are required whenever `cause_claim` is `true`. `unit_summaries` is optional on\neither `answered` shape. `cell_id` is required on every element of `answers`. No other keys, no\nprose outside the JSON object.\n'
def call(prompt: str) -> str:
"""Returns "FAIL" if a mid-stream SSE `error` event arrives, else "ok"."""
body = {
"model": "claude-haiku-4-5",
"max_tokens": 28000,
"temperature": 0.0,
"stream": True,
"messages": [{"role": "user", "content": prompt}],
"output_config": {"format": {"type": "json_schema", "schema": SCHEMA}},
}
headers = {
"x-api-key": os.environ["ANTHROPIC_API_KEY"],
"anthropic-version": "2023-06-01",
"content-type": "application/json",
}
with httpx.Client(timeout=60) as client:
with client.stream("POST", "https://api.anthropic.com/v1/messages", json=body, headers=headers) as r:
assert r.status_code == 200, f"unexpected HTTP status {r.status_code}"
for line in r.iter_lines():
if not line.startswith("data: "):
continue
event = json.loads(line[len("data: ") :])
if event.get("type") == "error":
return "FAIL"
return "ok"
if __name__ == "__main__":
print("Real failing prompt, unmodified :", [call(PROMPT_FAILING) for _ in range(3)])
print("Context-rows quotes removed only :", [call(PROMPT_CONTEXT_UNQUOTED) for _ in range(3)])
print("Cited source-code quotes removed :", [call(PROMPT_SOURCE_UNQUOTED) for _ in range(3)])
```
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.