The minimal reproduction
JSON.parse(undefined);
// SyntaxError: Unexpected token u in JSON at position 0
That's it. Any code path that funnels undefined into
JSON.parse produces this exact error. Your job is finding
which path.
The five places undefined sneaks in
1. localStorage / sessionStorage for a missing key
localStorage.getItem("settings") returns
null for a missing key — but if you wrapped it:
function loadSettings() {
const v = localStorage.getItem("settings");
if (!v) return; // implicit return undefined!
return JSON.parse(v);
}
const s = loadSettings();
const parsed = JSON.parse(s); // ← undefined here
Fix: return null explicitly, or check at the call site.
2. fetch() called on the Response object instead of the body
const res = await fetch("/api/user");
const data = JSON.parse(res); // ← wrong — res is a Response object
const data = JSON.parse(await res); // ← still wrong
const data = await res.json(); // ← correct
const data = JSON.parse(await res.text()); // ← also correct JSON.parse(response) stringifies the Response object to
something like "[object Response]" — the first char is
[, not u, so this usually triggers
"Unexpected token o" instead. But if you wrap a function that
sometimes returns the body and sometimes returns nothing, you can
hit u too.
3. React state read before the fetch resolves
const {data} = useFetch("/api/user");
const parsed = JSON.parse(data); // ← data is undefined on first render
Fix: guard. if (!data) return null; before parsing, or
only parse inside the effect that sets data.
4. process.env.SOMETHING for an unset env var
// .env doesn't have CONFIG set
const cfg = JSON.parse(process.env.CONFIG); // ← undefined → error
Fix: default at the read site. JSON.parse(process.env.CONFIG ?? "{}").
Or validate env at boot with a schema (Zod, valibot) that fails
loudly if a required var is missing.
5. Function that returns nothing on the unhappy path
function getJson(key) {
if (cache.has(key)) return cache.get(key);
// forgot to return on the miss path → implicit undefined
}
const x = JSON.parse(getJson("user")); // ← intermittent error
Fix: TypeScript with noImplicitReturns: true catches
these at build time. In plain JS, return null
explicitly on every branch.
The defensive parse helper
If you're parsing JSON from untrusted upstream code, wrap it:
function safeParse(v, fallback = null) {
if (typeof v !== "string" || !v) return fallback;
try {
return JSON.parse(v);
} catch {
return fallback;
}
}
Two layers: the type/empty check kills the undefined
case; the try/catch kills every other parse error. Returns your
fallback on either failure.
Related errors
- "Unexpected token o in JSON at position 1" — usually
[object Object] - "Unexpected token < in JSON at position 0" and other variants — full error guide
- Why JSON rejects comments and trailing commas
FAQ
Why specifically the letter 'u'?
JSON.parse stringifies its argument before parsing. When you pass undefined, JavaScript converts it to the string "undefined" — the parser sees a 'u' as the first character, doesn't recognize it as the start of any JSON value (which must start with {, [, ", a digit, t, f, or n), and throws at position 0.
Does this happen with null too?
No — null is valid JSON, so JSON.parse(null) succeeds (it stringifies to the four characters n-u-l-l, which is the JSON null literal). The error only triggers with undefined, or with any value whose toString starts with 'u' (rare in practice).
How do I detect this before parsing?
Three guards stack: (1) check the value isn't null or undefined, (2) check it's a string, (3) check it has length. `if (typeof v !== 'string' || !v) return null;` handles all three. Then JSON.parse only sees real strings.
Why does my test for localStorage break only in incognito mode?
Incognito clears localStorage between sessions, so keys you wrote in a prior test run aren't there. `localStorage.getItem('key')` returns null for missing keys — null stringifies to "null" (valid JSON), so this specific error is more often undefined-from-a-function than missing-from-storage. But the pattern is the same: guard before parsing.
Is there a difference between this and 'Unexpected token < in JSON at position 0'?
Yes. The 'u' variant means undefined; the '<' variant means the server returned HTML (often an error page). Different root causes, same surface symptom. Check the input string before assuming it's a JSON producer bug.