Workflow({name: ...}) fails with false "control characters" validation error; scriptPath/script work fine

Status Closed — duplicate
Maintainer reply None cached
Activity 1 comment · opened Aug 3, 2026 · closed Aug 15, 2026

Title

Workflow({name: ...}) fails with false "control characters" validation error; scriptPath/script work fine

Body

Summary

Calling the Workflow tool by name (a saved/predefined workflow) fails on every named workflow I've tried, even with trivial one-word args, with:

The permission handler returned updatedInput for Workflow that failed schema validation: [
  {
    "code": "custom",
    "path": ["script"],
    "message": "script contains control characters that would be hidden in the approval dialog"
  }
]
This is a configuration issue in your canUseTool callback, PermissionRequest hook, or permission-prompt tool, updatedInput must satisfy the tool's input schema. The tool input from the model was valid.

Passing the exact same script content via script (inline) or scriptPath (pointing at the same .js file on disk) works correctly. So the defect is specific to the name: -> registry-resolve -> approval-dialog rendering path, not to the script content or the Workflow engine in general.

Repro steps

  1. In a project with one or more saved workflows under .claude/workflows/*.js (each starting with export const meta = {...}).
  2. Call Workflow({ name: "<any-saved-workflow-name>", args: "test" }).
  3. Observe the error above, even though args is a single trivial word.
  4. Call Workflow({ scriptPath: "<same-file-path>", args: "test" }) instead, this succeeds (may then fail downstream on JSON.parse(args) if the script expects a JSON object, which is a separate, expected failure mode, the key point is it gets past the permission/validation step that blocks the name: call).
  5. Call Workflow({ script: "<inline literal of a trivial script>" }), this also succeeds.

What I ruled out before filing

  • Scanned the target .js script files programmatically for actual C0/C1 control characters, and separately for Unicode "hidden character" classes a linter would plausibly flag (zero-width space/joiner, bidi override/isolate characters U+202A-U+202E and U+2066-U+2069, BOM, soft hyphen). Zero hits in either pass.
  • Confirmed the failure reproduces across multiple distinct named workflows in the same project (not one corrupted file).
  • Checked the project's .claude/settings.json for any PermissionRequest hook or custom permission-prompt tool wired to the Workflow tool, none configured. The error message itself blames "your canUseTool callback, PermissionRequest hook, or permission-prompt tool," but no such hook exists in this project for this tool.
  • The failed run's stack trace (from a related but distinct crash while testing scriptPath, included for reference) pointed into the CLI's own bundled entrypoint (B:/~BUN/root/src/entrypoints/cli.js), suggesting the code path in question is internal to the CLI, not project-supplied.

Environment

  • Claude Code CLI (Windows, PowerShell primary shell)
  • Running in a git worktree checkout (.claude/worktrees/<name>), if relevant
  • Model: Sonnet 5

Impact / workaround

Not currently blocking, named workflows can be invoked instead via scriptPath pointing directly at the backing .claude/workflows/<name>.js file, which bypasses the broken path entirely. But this defeats the purpose of registering named/reusable workflows (name:-based invocation), which is presumably meant to be the primary, documented usage pattern.

Ask

Please look at whatever code path handles Workflow({name: ...}) resolution and hands the resolved script off to the permission-prompt/approval-dialog layer, it appears to introduce or misdetect control characters that aren't present in the source file, in a way that scriptPath/script invocations of the identical content do not trigger.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗