After testing the crash reproduction, I've found:
-
CLI Mode Behavior: The claude CLI with
--printmode doesn't actually crash with the problematic pattern. It generates the tool call but doesn't execute it (files aren't created). -
The Core Pattern: The crash is triggered by this specific combination:
cat > file.ts << 'EOF' console.log(`${variable?.method()}`); EOF
cat > /tmp/x.ts << 'EOF'
console.log(`${x?.toString()}`);
EOF- Claude Code's parser encounters
${x?.toString()}inside a heredoc - The parser incorrectly interprets this as bash variable substitution
- Even though the heredoc uses quoted delimiter (
'EOF'), which should prevent substitution - The error "Bad substitution: x?.toString" is a bash-style error message
The emoji (📝) appears to be coincidental. The crash is purely about the template literal pattern with optional chaining.
`${variable?.method()}` // This exact pattern inside heredoc${variable.method()}- No optional chaining${variable?.property}- No method call${variable || 'default'}- No optional chainingvariable?.method()- Outside template literal
When using Claude Code interactively and it executes:
cat > /tmp/file.ts << 'EOF'
const x = 42;
console.log(`Result: ${x?.toString()}`);
EOFResult: CRASH with "Bad substitution: x?.toString"
When using claude --print < prompt.txt:
- Tool call is generated correctly
- No actual execution occurs
- No crash happens
- This explains why our CLI tests didn't reproduce it
The crash occurs in the execution phase, not the generation phase. The minified code at:
file:///Users/alexanderriccio/.nvm/versions/node/v20.19.4/lib/node_modules/@anthropic-ai/claude-code/cli.js:80:79195
Contains a parser/executor that mishandles the template literal pattern within heredocs.
- Start Claude Code interactively
- Ask it to create a TypeScript file using the exact pattern
- Observe the crash
After any potential fix, verify these all work:
# Test 1: Basic optional chaining in template
cat > /tmp/test1.ts << 'EOF'
console.log(`${value?.toString()}`);
EOF
# Test 2: With multiple optional chains
cat > /tmp/test2.ts << 'EOF'
console.log(`${a?.b()} and ${c?.d()}`);
EOF
# Test 3: Complex nested case
cat > /tmp/test3.ts << 'EOF'
const result = `Data: ${obj?.method()?.substring(0, 10)}`;
EOFUntil fixed, these workarounds are confirmed:
- Extract before template:
const str = x?.toString(); console.log(\Value: ${str}`);` - Ternary operator:
console.log(\Value: ${x ? x.toString() : 'undefined'}`);` - Concatenation:
console.log("Value: " + x?.toString());