--json-schema file-path form (documented as primary) yields empty CLI output → no structured_output on the default-pinned CLI 2.1.160 #3

Closed
opened 2026-06-03 01:53:48 +02:00 by ryan · 0 comments
Owner

Summary

The README, examples/gitea-structured-output.yml, and the claude_args input description all present the file-path form --json-schema /path/to/schema.json as the primary way to get schema-validated output. But with the CLI version this action installs by default (2.1.160, per README → Version pins / action.yml), the Claude CLI's --json-schema flag only accepts an inline JSON schema string. Passing a file path makes the CLI exit 0 with zero output (empty stdout, empty stderr).

The action then finds no structured_output, hits its own fail-loud branch (base-action/src/run-claude.ts ~L402: "--json-schema was provided in claude_args but Claude did not return structured_output."), and the step fails. So a consumer who follows the documented path-form recipe gets a step that fails closed on every run — silently, with no useful diagnostic from the CLI.

Environment

  • Action ref: 13cde95 (gitea branch; the PR #2 / "claude_args passthrough + structured_output" tree).
  • CLI: 2.1.160 — the action's default install (action.yml: curl -fsSL https://claude.ai/install.sh | bash -s 2.1.160; README → Version pins).

Repro (controlled A/B — identical except path vs inline)

cat > /tmp/s.json <<'JSON'
{"type":"object","properties":{"decision":{"type":"string","enum":["pass","block"]},"summary":{"type":"string"}},"required":["decision","summary"],"additionalProperties":false}
JSON

# PATH form (what README / example / action.yml recommend):
claude -p --output-format json \
  --json-schema /tmp/s.json \
  --model claude-haiku-4-5-20251001 \
  'Return decision pass and a one-line summary.' </dev/null | wc -c
# -> 0     (exit 0, zero bytes, no stderr)

# INLINE form (schema content as the arg value):
claude -p --output-format json \
  --json-schema "$(cat /tmp/s.json)" \
  --model claude-haiku-4-5-20251001 \
  'Return decision pass and a one-line summary.' </dev/null | jq -c .structured_output
# -> {"decision":"pass","summary":"..."}   (works)

Same outcome with the action's actual mode (-p --verbose --output-format stream-json): the path form produces 0 bytes; the inline form's final {"type":"result",...} event carries structured_output. The CLI's own --help documents only the inline form — --json-schema <schema> with an inline JSON example — and never mentions a file path.

Impact

  • Silent failure: the path form returns exit 0, empty stdout, empty stderr. No "file not found", no schema error — nothing. The only signal is the action's downstream "did not return structured_output" failure.
  • The action's fail-loud guard is correct and working; the problem is that the documented happy path can never succeed on the default-pinned CLI.

Where the docs steer users to the broken form

  • README.md L112: "Use the file-path form (--json-schema /path/to/schema.json). An inline schema also works if single-quoted…" — recommends the path form as primary.
  • README.md L102 and examples/gitea-structured-output.yml L56: claude_args: --json-schema ${{ github.workspace }}/.gitea/review-schema.json.
  • action.yml (claude_args description): "e.g. --json-schema /path/to/schema.json".

Suggested fixes (any one resolves it)

  1. Docs: stop recommending the path form for the pinned 2.1.160; document the inline single-quoted form as the working one, and note the path form is unsupported on the default CLI. (README L112 / example / action.yml.)
  2. Action (preferred — keeps existing docs & examples correct): in prepareRunConfig, when the token following --json-schema is a path to an existing file, read the file and substitute its contents inline before forwarding to the CLI. This makes the documented path form actually work regardless of CLI quirks.
  3. CLI bump: if a newer pinned CLI accepts a file path for --json-schema, bump the default and document the minimum version.

Context

Found while debugging ryan/ourwhmcs's .gitea/workflows/claude-pr-auto-merge.yml, which (following this action's docs) passes --json-schema ${{ github.workspace }}/.workflow-main/.gitea/prompts/pr-review.schema.json — the path form — and would therefore produce no verdict on the first PR that reaches the review step.

## Summary The README, `examples/gitea-structured-output.yml`, and the `claude_args` input description all present the **file-path** form `--json-schema /path/to/schema.json` as the primary way to get schema-validated output. But with the CLI version this action installs **by default** (`2.1.160`, per README → Version pins / `action.yml`), the Claude CLI's `--json-schema` flag only accepts an **inline JSON schema string**. Passing a **file path** makes the CLI exit `0` with **zero output** (empty stdout, empty stderr). The action then finds no `structured_output`, hits its own fail-loud branch (`base-action/src/run-claude.ts` ~L402: *"--json-schema was provided in claude_args but Claude did not return structured_output."*), and the step fails. So a consumer who follows the documented path-form recipe gets a step that **fails closed on every run** — silently, with no useful diagnostic from the CLI. ## Environment - Action ref: `13cde95` (gitea branch; the PR #2 / "claude_args passthrough + structured_output" tree). - CLI: `2.1.160` — the action's default install (`action.yml`: `curl -fsSL https://claude.ai/install.sh | bash -s 2.1.160`; README → Version pins). ## Repro (controlled A/B — identical except path vs inline) ```bash cat > /tmp/s.json <<'JSON' {"type":"object","properties":{"decision":{"type":"string","enum":["pass","block"]},"summary":{"type":"string"}},"required":["decision","summary"],"additionalProperties":false} JSON # PATH form (what README / example / action.yml recommend): claude -p --output-format json \ --json-schema /tmp/s.json \ --model claude-haiku-4-5-20251001 \ 'Return decision pass and a one-line summary.' </dev/null | wc -c # -> 0 (exit 0, zero bytes, no stderr) # INLINE form (schema content as the arg value): claude -p --output-format json \ --json-schema "$(cat /tmp/s.json)" \ --model claude-haiku-4-5-20251001 \ 'Return decision pass and a one-line summary.' </dev/null | jq -c .structured_output # -> {"decision":"pass","summary":"..."} (works) ``` Same outcome with the action's actual mode (`-p --verbose --output-format stream-json`): the **path** form produces 0 bytes; the **inline** form's final `{"type":"result",...}` event carries `structured_output`. The CLI's own `--help` documents only the inline form — `--json-schema <schema>` with an inline JSON example — and never mentions a file path. ## Impact - **Silent failure**: the path form returns exit `0`, empty stdout, empty stderr. No "file not found", no schema error — nothing. The only signal is the action's downstream *"did not return structured_output"* failure. - The action's fail-loud guard is correct and working; the problem is that the **documented happy path can never succeed** on the default-pinned CLI. ## Where the docs steer users to the broken form - `README.md` L112: *"**Use the file-path form** (`--json-schema /path/to/schema.json`). An inline schema also works if single-quoted…"* — recommends the path form as primary. - `README.md` L102 and `examples/gitea-structured-output.yml` L56: `claude_args: --json-schema ${{ github.workspace }}/.gitea/review-schema.json`. - `action.yml` (`claude_args` description): *"e.g. `--json-schema /path/to/schema.json`"*. ## Suggested fixes (any one resolves it) 1. **Docs**: stop recommending the path form for the pinned `2.1.160`; document the **inline single-quoted** form as the working one, and note the path form is unsupported on the default CLI. (README L112 / example / `action.yml`.) 2. **Action (preferred — keeps existing docs & examples correct)**: in `prepareRunConfig`, when the token following `--json-schema` is a path to an existing file, read the file and substitute its contents inline before forwarding to the CLI. This makes the documented path form actually work regardless of CLI quirks. 3. **CLI bump**: if a newer pinned CLI accepts a file path for `--json-schema`, bump the default and document the minimum version. ## Context Found while debugging `ryan/ourwhmcs`'s `.gitea/workflows/claude-pr-auto-merge.yml`, which (following this action's docs) passes `--json-schema ${{ github.workspace }}/.workflow-main/.gitea/prompts/pr-review.schema.json` — the path form — and would therefore produce no verdict on the first PR that reaches the review step.
ryan closed this issue 2026-06-03 02:52:33 +02:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ryan/claude-code-gitea-action#3