Workflow({scriptPath}) false-fails "hidden control characters" on a clean file with CRLF line endings

Open 💬 1 comment Opened Jul 3, 2026 by VelociCopter

Workflow({scriptPath}) false-fails "hidden control characters" on a clean file with CRLF line endings

Summary

Calling the Workflow tool with {scriptPath, args} fails with a permission-validation error on a script file that contains no suspicious content — only normal Windows CRLF (\r\n) line endings. The identical script content passed inline via {script, args} works fine.

Error

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"}]

Repro

  1. On a Windows checkout with core.autocrlf=true (the Windows git default), check out a repo containing a .claude/workflows/*.mjs script that is committed as LF-only. Git will check it out locally as CRLF.
  2. Verify the file has no actually-hidden control characters: no \x00-\x1F besides the CRLF pairs themselves, no C1 controls, no zero-width/bidi Unicode, no lone \r (i.e. every \r is immediately followed by \n).
  3. Call Workflow({ scriptPath: '.claude/workflows/<file>.mjs', args: {...} }).
  4. Observe the schema-validation error above, blocking the call entirely — no approval dialog is even shown.
  5. Call Workflow({ script: <same file's contents as a string>, args: {...} }) instead — this succeeds. (Note: reading the file with a normal file-read tool and passing its string content this way naturally normalizes to \n-only, since the tool-call JSON payload itself has no raw \r bytes.)

This reproduced identically across two separate attempts with different args payloads (one containing escaped \n and Unicode bullets/em-dashes in prompt strings, one plain-ASCII) — ruling out args-content as the trigger. It is specific to the scriptPathscript resolution path.

Expected

A file whose only "control characters" are \r bytes that are each immediately followed by \n (i.e. valid CRLF line endings — completely normal on any Windows checkout, not a hidden/invisible character in any editor or terminal) should not trip a "hidden control characters" safety check. Either:

  • normalize CRLF → LF (or otherwise treat \r\n as equivalent to \n) before running the hidden-character scan, matching how the inline script: path behaves, or
  • exclude \r immediately followed by \n from the flagged character set, only flagging genuinely anomalous control characters (lone \r, C0/C1 controls other than tab/newline, zero-width/bidi Unicode, etc).

Impact

This defeats the specific convenience the scriptPath mode advertises — iterating on a saved workflow script on disk without resending the full script text on every call — for any repo checked out on Windows with default core.autocrlf settings (extremely common). The only workaround is to always read the file and pass its content inline via script:, which reintroduces the token cost scriptPath exists to avoid.

Environment

  • OS: Windows 11
  • Git: core.autocrlf=true (default on Windows)
  • Workflow tool available via Claude Code / Agent SDK

View original on GitHub ↗

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