[BUG] /goal Stop hook fails with "JSON validation failed" when prompt-based hook returns markdown-fenced JSON; goal never auto-clears

Resolved 💬 2 comments Opened May 13, 2026 by liby Closed May 15, 2026

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:

  1. Every turn end shows Stop hook error: JSON validation failed to the user.
  2. The goal is never auto-cleared, even after the hook has internally judged it complete.
  3. The user is forced to run /goal clear manually, which contradicts the documented behavior that goals auto-clear once met.

Repro

  1. Run /goal "Set your own goal for the current task. Make it outcome-based, include validation criteria, then pursue it until complete."
  2. Have Claude complete the implied task and report success.
  3. End the turn.

Actual

A hook attachment is recorded in the session JSONL with:

  • hookName: "Stop"
  • exitCode: 1
  • stderr: "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 (/goal Stop 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

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗