[BUG] /goal Stop hook fails with "JSON validation failed" when prompt-based hook returns markdown-fenced JSON; goal never auto-clears
Summary
When using /goal to set a session-scoped Stop hook, the hook (which runs Claude as a sub-process to evaluate goal completion) outputs JSON wrapped in a markdown code fence. The Stop hook validator rejects this and emits JSON validation failed, even though the inner JSON is structurally valid and reports "ok": true. As a result:
- Every turn end shows
Stop hook error: JSON validation failedto the user. - The goal is never auto-cleared, even after the hook has internally judged it complete.
- The user is forced to run
/goal clearmanually, which contradicts the documented behavior that goals auto-clear once met.
Repro
- Run
/goal "Set your own goal for the current task. Make it outcome-based, include validation criteria, then pursue it until complete." - Have Claude complete the implied task and report success.
- End the turn.
Actual
A hook attachment is recorded in the session JSONL with:
hookName: "Stop"exitCode: 1stderr: "JSON validation failed"stdout:
````
json`
{
"ok": true,
"reason": "Goal was explicitly set and pursued to near-completion. ..."
}
```
The user-facing surface shows:
⏺ Ran 2 stop hooks
⎿ ...
⎿ Stop hook error: JSON validation failed
Expected
Either:
a. The /goal hook prompt should constrain Claude to emit raw JSON only (no markdown fence), e.g. via the structured outputs / constrained decoding path suggested in #22750, or
b. The Stop hook validator should tolerate fenced JSON (strip `json and trailing fence before parsing).
When the hook reports "ok": true, the goal should be auto-cleared per documented behavior.
Evidence (session JSONL hook record, redacted)
{
"attachment": {
"type": "hook_non_blocking_error",
"hookName": "Stop",
"hookEvent": "Stop",
"stderr": "JSON validation failed",
"stdout": "```json\n{\n \"ok\": true,\n \"reason\": \"<truncated>\"\n}\n```",
"exitCode": 1,
"command": "Set your own goal for <task>. Make it outcome-based, include validation criteria, then pursue it until complete.",
"durationMs": 5631
},
"type": "attachment",
"hookName": "Stop"
}
Related
- #22750 — same root cause (prompt hook returning markdown-fenced JSON), closed; this issue extends it to the
/goal-specific symptom of failed auto-clear - #11947 — adjacent (Stop hook JSON schema mismatch), closed
- #58348 — adjacent (
/goalStop hook infinite loop), open
Environment
- Claude Code version:
2.1.139 - OS: macOS (darwin 25.4.0)
- Reproducible: 100% across multiple turns in the same session
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗