Corrective Action: CRLF Line Endings Causing Silent Token Loading Failures Across Infrastructure

Corrective Action: CRLF Line Endings Causing Silent Token Loading Failures Across Infrastructure

Date: March 8, 2026 Category: Data Format Mismatch + Verification Failure Impact: Token loading failures across every session, cascading into credential leakage into settings.local.json, MCP tool unavailability, and wasted session time Resolution Time: ~90 minutes from diagnosis to fix (but the problem existed since system creation)


Incident

What Happened

Every .env file created on the Windows/WSL system had CRLF (\r\n) line endings. Bash source .env silently fails to parse CRLF files — it reads the \r as part of the value, making every token invalid. This caused cascading failures: HubSpot API calls failed with "invalid token," MCP servers couldn't authenticate, and Claude Code sessions repeatedly couldn't locate working credentials.

The secondary effect was more insidious: when Claude couldn't find tokens through normal channels, it hardcoded them inline in bash commands (e.g., HUBSPOT_ACCESS_TOKEN=pat-na1-... node script.js). Claude Code then saved these approved commands — credentials included — as permanent permission rules in settings.local.json. Three leaked credentials were found.

Timeline

Time Event
System creation (Feb 2026) .env files created on Windows — all have CRLF line endings
Every session since source .env silently fails; tokens appear loaded but contain trailing \r
Every session since Claude falls back to hardcoding tokens inline in bash commands
Unknown dates (3 incidents) Claude Code saves inline-token bash commands as permanent permission rules
Mar 8 ~10:30 AM CT /weekly-plan session: token loading fails, HubSpot MCP unavailable
Mar 8 ~11:00 AM CT Root cause identified: CRLF in .env files
Mar 8 ~11:30 AM CT 29 .env files converted to LF; .editorconfig created; 3 leaked credentials scrubbed from settings.local.json
Mar 8 ~12:00 PM CT Fix committed and pushed (.editorconfig.env files are gitignored)

Root Cause

The failure mechanism has three layers:

Layer 1 — File Creation: Chris uses Windows (D: drive mounted as /mnt/d in WSL). Text editors on Windows create files with \r\n (CRLF) line endings by default. Every .env file in the system — monorepo root, per-client directories, and the D drive root — had CRLF endings.

Layer 2 — Silent Parse Failure: Bash source .env on a CRLF file reads TOKEN=value\r instead of TOKEN=value. The trailing \r is invisible but makes the token invalid for API calls. There is no error message. The variable appears to be set. echo $TOKEN shows the value. But every HTTP request using it gets a 401.

Layer 3 — Cascading Credential Leakage: When normal token loading fails, Claude Code adapts by inlining credentials directly in bash commands: HUBSPOT_ACCESS_TOKEN=pat-na1-xxx node script.js. When the user approves this command, Claude Code saves it as a permission rule in settings.local.json — credentials and all. This creates a permanent, plaintext record of every token that was ever hardcoded as a workaround.

Category: Data Format Mismatch + Verification Failure

This is a cross-platform data format issue that manifests as a silent failure. The .gitattributes file already enforced LF for tracked files, but .env files are gitignored — they were never normalized.

The verification failure component: no session startup check ever validated that loaded tokens were actually functional. Token loading was assumed to succeed if the file existed.


Fix Applied

Immediate Resolution

  1. Converted all 29 .env and .env.example files from CRLF to LF using sed -i 's/\r$//'
  2. Created .editorconfig at monorepo root to prevent future CRLF contamination
  3. Scrubbed 3 leaked credentials from settings.local.json (replaced exact tokens with wildcard patterns)
  4. Removed 1 malformed permission entry from settings.local.json

Code/Configuration Changes

File Change
/mnt/d/Projects/value-first-operations/.editorconfig Created — enforces end_of_line = lf for all files
/mnt/d/.env CRLF → LF (57 lines)
/mnt/d/Projects/value-first-operations/clients/vf-team/.env CRLF → LF
/mnt/d/Projects/value-first-operations/clients/paragon/.env CRLF → LF
26 .env.example files across monorepo CRLF → LF on disk
/mnt/d/.claude/settings.local.json Scrubbed 3 hardcoded credentials, removed malformed entry

Verification

# Confirmed all .env files now have LF endings
file /mnt/d/Projects/value-first-operations/.env
# → ASCII text (no "CRLF" designation)

file /mnt/d/Projects/value-first-operations/clients/vf-team/.env
# → ASCII text

# Confirmed .editorconfig committed and pushed
git -C /mnt/d/Projects/value-first-operations log --oneline -1
# → .editorconfig: Enforce LF line endings across monorepo

Prevention Measures

Rules Added

Layer File Rule
Critical Lessons memory/MEMORY.md "CRLF line endings in .env files cause silent token failures. .editorconfig enforces LF. If new .env files appear with CRLF, convert with sed -i 's/\r$//'. Windows-created files are always suspect."
Enforcement skills/enforcement/vf-self-correction.md Detection trigger: if token loading fails or API returns 401, check file line endings before assuming token is invalid
Enforcement CLAUDE.md Step 4 Enforcement skills must be read at session start — enforcement loading is now MANDATORY, not optional
Operations memory/operations.md Infrastructure health note: CRLF prevention active via .editorconfig + .gitattributes
Instruction Optimizer agents/instruction-optimizer/data/incident-log.json Incident logged for pattern analysis

Detection Triggers

  1. If source .env produces no errors but API calls return 401 → Check file path/to/.env for CRLF designation
  2. If Claude hardcodes a token inline in a bash command → The normal token loading path is broken. Stop and fix the root cause instead of working around it.
  3. If settings.local.json contains literal API tokens → Credential leakage from a broken token loading path. Scrub immediately and fix the underlying issue.

Lessons

The most dangerous bugs are the silent ones. CRLF line endings don't throw errors — they corrupt values invisibly, and every workaround (hardcoding tokens) creates a new problem (credential leakage). Cross-platform development on Windows/WSL requires explicit encoding enforcement at every layer: .gitattributes for tracked files, .editorconfig for all files, and awareness that gitignored files (.env) fall through both nets.

The deeper lesson: when AI systems can't find the credentials they need through normal channels, they improvise — and the improvisations leave artifacts. Leaked credentials in settings.local.json weren't a security incident; they were symptoms of infrastructure failure. Treat improvised workarounds as diagnostic signals, not as the problem itself.


Related Incidents

  • HubSpot Broadcast API no draft mode (Feb 18) — Another "silent behavior" incident where missing configuration caused unintended side effects
  • Substring date matching (Feb 21) — Another data format issue where invisible differences in strings caused wrong matches
  • Portal Documents filter mismatch (Mar 2) — Exact-match requirement where a slightly wrong value caused invisible failures