Skip to content

fix(host-api-wrapper): return undefined from readJSON for absent keys#226

Merged
cuteWarmFrog merged 2 commits into
mainfrom
fix/localstorage-readjson-empty-value
Jul 3, 2026
Merged

fix(host-api-wrapper): return undefined from readJSON for absent keys#226
cuteWarmFrog merged 2 commits into
mainfrom
fix/localstorage-readjson-empty-value

Conversation

@cuteWarmFrog

Copy link
Copy Markdown
Contributor

Problem

hostLocalStorage.readJSON(key) piped the read straight into JSON.parse:

readBytes(key).then(bytes => textDecoder.decode(bytes)).then(JSON.parse);

A never-written (or cleared) key reads back as undefined, and an empty value decodes to ''. JSON.parse('') throws SyntaxError: Unexpected end of JSON input — so the normal first-run path (key not written yet) surfaced as an error. Every product using readJSON for optional/first-run data had to wrap reads in a try/catch just to get a default.

This was observed in the wild via a product's forwarded console: [coinflipgame03.dot] Error reading from localStorage: SyntaxError: Unexpected end of JSON input on first launch, before flip_stats had ever been saved.

Fix

Guard the absent/empty case in readJSON and return undefined instead of parsing:

const bytes = await readBytes(key);
if (bytes === undefined || bytes.length === 0) return undefined;
return JSON.parse(textDecoder.decode(bytes));
  • Missing / cleared key → undefined
  • Empty stored value → undefined
  • Valid JSON → parsed (unchanged)
  • Genuinely malformed (non-empty) data still throws — real corruption stays visible

Tests

Added two cases to __tests__/hostApi/localStorage.spec.ts (missing key, empty value). Both reproduced the exact SyntaxError before the fix and pass after. Full suite green (673 passing), lint + typecheck clean.

https://claude.ai/code/session_01SqQTbeaAxijCffoCcp6Hyt

`readJSON` piped the read straight into `JSON.parse`, but a never-written
or cleared key reads back as `undefined` and an empty value decodes to
`''` — so `JSON.parse('')` threw `SyntaxError: Unexpected end of JSON
input` on the normal first-run path. Products had to wrap every read in a
try/catch just to handle "no value yet".

Guard the absent/empty case and return `undefined` instead. Genuinely
malformed (non-empty) data still throws, so real corruption stays visible.

Claude-Session: https://claude.ai/code/session_01SqQTbeaAxijCffoCcp6Hyt
@cuteWarmFrog cuteWarmFrog self-assigned this Jul 3, 2026
Comment thread packages/host-api-wrapper/src/localStorage.ts Outdated
@cuteWarmFrog cuteWarmFrog merged commit 592dc4b into main Jul 3, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants