Stale localStorage credentials (`jwt` / `auth` / `adminAuth` / `userJwt` /
`access_token`) can be the empty string, the literal string `"undefined"`,
or carry a stray newline / control character left over from an older
build. axios + undici reject these eagerly with `Invalid character in
header content ["Authorization"]`, so every API call crashes client-side
before reaching the worker.
This adds two tiny helpers in `frontend/src/utils/headers.js`:
- `safeHeaderValue(v)` returns the trimmed value when it is a non-empty
string with no control chars (per RFC 7230) and no `"undefined"` /
`"null"` sentinel; otherwise `undefined`.
- `safeBearerHeader(jwt)` wraps a safe JWT with `Bearer `, otherwise
`undefined`.
`apiFetch` builds the headers object incrementally and only sets each
auth header when its value is safe. Missing/unsafe credentials now drop
out cleanly and the worker returns a normal 401, which the existing
`response.status === 401` flow already handles by surfacing the auth
prompt — the same UX users see on a fresh session.
Tests: `frontend/src/utils/__tests__/headers.test.js` adds 9 vitest
cases covering safe input, sentinel strings, control chars (\\n / \\r /
\\t / NUL / 0x1F / DEL), trimming, and `Bearer` construction. Build
(`pnpm build`) and tests (`pnpm test`) both pass.
Co-authored-by: voidborne-d <voidborne.d@agentmail.to>
Co-authored-by: Dream Hunter <dreamhunter2333@gmail.com>