codex-cli backend returns duplicate JSON and breaks verification
Description
When using FM-Agent with the local Codex CLI backend, verification fails because the captured response contains the same JSON object twice.
Configuration
[llm]
name = ""
...
backend = "codex-cli"
...
Actual behavior
Phase generation, domain-context generation, and spec generation complete successfully. Verification then retries five times with:
LLM response contains multiple JSON values
The corresponding event in fm_agent/trace/events.jsonl is recorded as:
{
"stage": "verification",
"status": "format_error",
"metadata": {
"attempt": 1,
"parse_error": "LLM response contains multiple JSON values"
}
}
The captured output contains duplicate JSON:
{"post_condition":"..."}
tokens used
4,250
{"post_condition":"..."}
The final verification result is:
{
"verdict": "ERROR",
"gaps": null,
"error": "Failed to generate post-condition for block 1."
}
Cause analysis
run_agent_for_messages() in src/cli_backend.py starts the CLI with stderr redirected into stdout:
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
Codex CLI writes its terminal/session transcript to stderr and its final response to stdout. The transcript can contain a rendered copy of the final answer, while stdout contains the final answer again. After FM-Agent merges both streams, the response passed to the structured-output parser contains two valid JSON objects.
_parse_json_response() in src/llm_client.py deliberately rejects responses containing more than one JSON value:
if len(values) > 1:
raise ValueError("LLM response contains multiple JSON values")
The attempt is therefore recorded as format_error. After five unsuccessful retries, no post-condition is returned, so the reasoner reports Failed to generate post-condition for block 1. and the final verification verdict becomes ERROR.
The retry path also appends the complete previous CLI response to the next request. Because that response contains the terminal transcript and duplicate JSON, the request context and token usage grow substantially on every retry.
Expected behavior
The successful Codex response should be parsed once, allowing verification to complete with MATCH or MISMATCH.
Environment
- Backend:
codex-cli
- Model: local Codex default
- Codex CLI:
0.144.6
codex-clibackend returns duplicate JSON and breaks verificationDescription
When using FM-Agent with the local Codex CLI backend, verification fails because the captured response contains the same JSON object twice.
Configuration
Actual behavior
Phase generation, domain-context generation, and spec generation complete successfully. Verification then retries five times with:
The corresponding event in
fm_agent/trace/events.jsonlis recorded as:{ "stage": "verification", "status": "format_error", "metadata": { "attempt": 1, "parse_error": "LLM response contains multiple JSON values" } }The captured output contains duplicate JSON:
The final verification result is:
{ "verdict": "ERROR", "gaps": null, "error": "Failed to generate post-condition for block 1." }Cause analysis
run_agent_for_messages()insrc/cli_backend.pystarts the CLI with stderr redirected into stdout:Codex CLI writes its terminal/session transcript to stderr and its final response to stdout. The transcript can contain a rendered copy of the final answer, while stdout contains the final answer again. After FM-Agent merges both streams, the response passed to the structured-output parser contains two valid JSON objects.
_parse_json_response()insrc/llm_client.pydeliberately rejects responses containing more than one JSON value:The attempt is therefore recorded as
format_error. After five unsuccessful retries, no post-condition is returned, so the reasoner reportsFailed to generate post-condition for block 1.and the final verification verdict becomesERROR.The retry path also appends the complete previous CLI response to the next request. Because that response contains the terminal transcript and duplicate JSON, the request context and token usage grow substantially on every retry.
Expected behavior
The successful Codex response should be parsed once, allowing verification to complete with
MATCHorMISMATCH.Environment
codex-cli0.144.6