Workflow() tool rejects a clean ~79 KB script with control characters false positive
Titre
Workflow() tool rejects a clean ~79 KB script with "script contains control characters that would be hidden in the approval dialog" (false positive)
Description
The Workflow() tool (Claude Agent SDK / Claude Code) consistently refuses to run a specific .js workflow script, failing before execution even starts, 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 happens when invoking via scriptPath (not inline script):
Workflow({
scriptPath: "<path>\\workflows\\traiter-cr.js",
args: "CR-243-....md"
})
Why this looks like a false positive, not an actual control-character issue
The target file (traiter-cr.js, ~78,798 bytes / 1079 lines, UTF-8 with a leading BOM) was scanned exhaustively for any character that could legitimately trigger this check:
- Full character-by-character scan of Unicode categories
Control,Format,Surrogate,PrivateUse,OtherNotAssigned(including astral-plane characters/emoji decoded correctly as UTF-32 surrogate pairs) → 0 matches across all 77,118 decoded characters. - Targeted search for zero-width / bidi / invisible characters: U+200B, U+200C, U+200D, U+2060, U+FEFF (outside byte offset 0), U+2028, U+2029, U+00A0, U+200E, U+200F, U+202A–U+202E, U+2066–U+2069, U+00AD, and ESC (U+001B, including inside JS string literals) → 0 matches.
- No unpaired UTF-16 surrogates.
- Byte-level scan for raw bytes < 0x20 (excluding tab/LF/CR) and DEL (0x7F) → 0 matches.
- BOM present only at file start (
EF BB BF), no BOM occurrences mid-file.
Reproduction pattern observed
- The error is 100% reproducible across multiple separate attempts, with different
args(different target files referenced by the workflow), over multiple sessions — including after fully restarting the Claude Desktop app (a pending update was applied at restart; the bug persisted identically afterward). - The same script had worked successfully earlier in an earlier session, then started failing after later (legitimate, content-only) edits to the file — i.e., this is not "the file has always been broken," something about editing/re-saving triggered it.
- Comparison with two other workflow scripts in the same directory that do run successfully via the same tool:
| File | Size | Result |
|---|---|---|
| traiter-cr.js | 78,798 bytes / 1079 lines | fails |
| revue-globale.js | 13,729 bytes | succeeds |
| traiter-toutes-cr.js | 5,120 bytes | succeeds |
The failing file is roughly 6× and 15× larger than the two working files, respectively. This size correlation (rather than any specific character found) is the strongest available signal.
Hypothesis
The permission-handler's script preview/validation step (used to render the approval dialog) may buffer or truncate the script content for display, and on large files this could be splitting a multi-byte UTF-8 sequence (e.g. an emoji or accented character) at the wrong byte boundary — producing an invalid/control-looking sequence that does not exist in the actual source file, but appears in whatever buffer the validation logic inspects. This could not be fully confirmed without visibility into the SDK's internal handling, but no alternative explanation survived the content-level investigation above.
Impact
Any workflow script that grows large enough to hit this (apparently size-correlated) threshold becomes permanently unusable via the Workflow() tool, with no actionable fix available on the content side — repeated content edits, restarts, and retries all reproduce the identical failure.
Environment
- Claude Code / Claude Agent SDK, Windows 11
- Script encoding: UTF-8 with BOM, CRLF/LF mixed (standard for the repo)