Skip to content

fix(ui): guard against non-functional localStorage in Node/SSR environment (fixes #5379)#6226

Open
asheesh-devops wants to merge 1 commit intokeephq:mainfrom
asheesh-devops:fix/5379-localstorage-ssr-guard
Open

fix(ui): guard against non-functional localStorage in Node/SSR environment (fixes #5379)#6226
asheesh-devops wants to merge 1 commit intokeephq:mainfrom
asheesh-devops:fix/5379-localstorage-ssr-guard

Conversation

@asheesh-devops
Copy link
Copy Markdown

@asheesh-devops asheesh-devops commented Apr 8, 2026

Summary

Fixes TypeError: localStorage.getItem is not a function that crashes the /signin page with a 500 error on first run of docker compose -f docker-compose.dev.yml up.

Root Cause

When Node.js is started with the --localstorage-file flag without a valid path (as happens in the Docker dev setup), the global localStorage object exists but getItem/setItem are not proper functions. The existing guard in useLocalStorage hook only checks typeof localStorage === "undefined" which passes, leading to:

TypeError: localStorage.getItem is not a function
⨯ [TypeError: localStorage.getItem is not a function] { page: '/signin' }

The Fix

Add additional type checks for getItem/setItem being actual functions:

// Before (broken):
if (typeof window === "undefined" || typeof localStorage === "undefined") {
  return null;
}
return localStorage.getItem(`keephq-${key}`);  // FAILS - getItem is not a function

// After (fixed):
if (
  typeof window === "undefined" ||
  typeof localStorage === "undefined" ||
  typeof localStorage.getItem !== "function"
) {
  return null;  // Gracefully falls back
}
return localStorage.getItem(`keephq-${key}`);  // Works!

Changes

  • keep-ui/utils/hooks/useLocalStorage.ts — enhanced SSR guards in getSnapshot() and setLocalStorageValue()

Testing

  • Verified this is the central useLocalStorage hook used by 26+ components across the app
  • The useSyncExternalStore third argument (server snapshot) returns JSON.stringify(initialValue) so the hook gracefully degrades during SSR
  • Confirmed no other direct localStorage calls in the signin page flow — all go through this hook

Fixes #5379

When Node.js is started with --localstorage-file without a valid path,
the global localStorage object exists but getItem/setItem are not
functions. The existing typeof localStorage check passes, causing
TypeError: localStorage.getItem is not a function on the /signin page.

Added typeof localStorage.getItem/setItem !== 'function' checks to
handle this edge case in the useLocalStorage hook.

Closes keephq#5379
@dosubot dosubot bot added size:S This PR changes 10-29 lines, ignoring generated files. Bug Something isn't working javascript Pull requests that update Javascript code labels Apr 8, 2026
@asheesh-devops asheesh-devops changed the title fix: guard against non-functional localStorage in Node/SSR environment fix(ui): guard against non-functional localStorage in Node/SSR environment (fixes #5379) Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working javascript Pull requests that update Javascript code size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🐛 Bug]:the frontend container (keep-frontend-dev-1) throws a TypeError: localStorage.getItem is not a function.

1 participant