claude-code - 💡(How to fix) Fix [BUG] /goal Stop hook fails with "JSON validation failed" when prompt-based hook returns markdown-fenced JSON; goal never auto-clears

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

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.

Error Message

⏺ Ran 2 stop hooks ⎿ ... ⎿ Stop hook error: JSON validation failed

Root Cause

  • #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

Code Example



---



---

Ran 2 stop hooks
...
Stop hook error: JSON validation failed

---

{
  "attachment": {
    "type": "hook_non_blocking_error",
    "hookName": "Stop",
    "hookEvent": "Stop",
    "stderr": "JSON validation failed",
    "stdout": "
RAW_BUFFERClick to expand / collapse

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

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING