Skip to content

shell-quote treats # in claude_args values as comments, causing null values and AJV crash #980

@f1sherman

Description

@f1sherman

Describe the bug

The action uses shell-quote's parse() to process claude_args. When a flag value contains # (even if backslash-escaped), shell-quote treats it as a comment and returns a {comment: "..."} object instead of a string. The action's .filter(arg => typeof arg === 'string') then drops it, leaving the flag with no value, which the parser sets to null. This null propagates into the SDK config and triggers an AJV schema validation crash before any API calls are made.

To Reproduce

  1. Create a workflow using anthropics/claude-code-action@v1
  2. Set claude_args with a value containing # (note: # is preserved inside YAML literal blocks |):
    claude_args: |
      --model claude-sonnet-4-5-20250929
      --append-system-prompt \#\ Use\ best\ practices
  3. Run the workflow
  4. The action crashes with an AJV validation error at $0 cost

You can verify the parsing behavior locally with Node.js:

const { parse } = require('shell-quote');

// Backslash escaping collapses \# to #, which shell-quote treats as a comment
parse('--append-system-prompt \\#\\ Use\\ best\\ practices')
// → ["--append-system-prompt", {comment: " Use best practices"}]

// The action filters out non-string objects:
// → ["--append-system-prompt"]
// → parser sets: {"append-system-prompt": null}

// Unescaped # is also treated as a comment
parse('--append-system-prompt # Use best practices')
// → ["--append-system-prompt", {comment: " Use best practices"}]

// Double-quoted values work correctly
parse('--append-system-prompt "# Use best practices"')
// → ["--append-system-prompt", "# Use best practices"]

Expected behavior

The action should parse # in flag values as literal text, not as a comment. At minimum, null values should be rejected with a clear error instead of being passed to the SDK.

Workflow yml file

name: Repro AJV Crash
on: workflow_dispatch

jobs:
  repro:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: "Say hello"
          claude_args: |
            --model claude-sonnet-4-5-20250929
            --append-system-prompt \#\ Use\ best\ practices

API Provider

[x] Anthropic First-Party API (default)
[ ] AWS Bedrock
[ ] GCP Vertex

Additional context

The root cause is in base-action/src/parse-sdk-options.ts, parseClaudeArgsToExtraArgs():

const args = parseShellArgs(claudeArgs).filter(
  (arg): arg is string => typeof arg === "string",  // drops {comment: "..."} objects
);

When the loop reaches a --flag whose value was dropped, nextArg is undefined, so the parser falls into the boolean flag branch and sets result[flag] = null. The resulting config:

{
  "model": "claude-sonnet-4-5-20250929",
  "append-system-prompt": null
}

This null triggers the AJV crash:

SDK execution error: 14 | depsCount: ${X},
15 | deps: ${Y}}`};var Tj={keyword:"dependencies",type:"object",schemaType:"object",...
error: Claude Code process exited with code 1

Workaround: Double-quote values containing #:

# BROKEN — backslash escaping still results in # after shell-quote parses it
claude_args: |
  --append-system-prompt \#\ Use\ best\ practices

# WORKS — double quotes prevent # from being treated as a comment
claude_args: |
  --append-system-prompt "# Use best practices"

May be related to #947, #892, #852 which report the same AJV error signature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdev-experiencep2Non-showstopper bug or popular feature request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions