Troubleshooting
This page catalogs the most common problems encountered when developing, testing, or operating Neemias, with step-by-step solutions.
If the problem is not listed here, also check the Quickstart, deploy environments, and the README.
1. "Initialization error" / "Cannot access storage"
Symptom
When opening the frontend, a generic initialization error message appears, or the browser console shows IndexedDB-related errors such as DexieError, UnknownError, AbortError, or versionchange transaction.
Cause
The local IndexedDB database (neemias-db) may be corrupted — common during development, after Dexie schema changes (db.ts), or migrations that did not apply correctly. Dexie keeps open connections and, in some scenarios (fast reload, multiple tabs), the versionchange transaction may fail.
The database may also get stuck if another tab on the same domain has an active Dexie connection.
Solution
- Open Chrome DevTools (F12).
- Go to the Application tab → Storage → IndexedDB.
- Locate the
neemias-dbdatabase. - Right-click → Delete database.
- Close all other tabs on the same domain (
localhost:5173or the deploy domain) before reloading. - Reload the page (F5).
If the problem occurs in automated tests (Playwright/Puppeteer), navigate to about:blank before deleting — this closes the active Dexie connection:
// Example: IndexedDB cleanup in Playwright tests
await page.goto("about:blank");
await page.evaluate(() => indexedDB.deleteDatabase("neemias-db"));
await page.goto("http://localhost:5173");Note: In a development environment, after clearing IndexedDB the seed will run again on the next load (if
DEPLOY_ENVis notprod), repopulating 500 students, 7 classes, and 20 nuclei.
2. Login fails with "Invalid credentials"
Symptom
When trying to log in with admin@neemias.local / senha123 (or another seed credential), the backend returns an invalid credentials error.
Checks
2a. Has the backend seed been run?
The backend (Worker + D1) needs to have the seed run separately from the frontend. The frontend seed populates IndexedDB; the backend seed populates D1.
# Check if migrations have been applied
pnpm db:migrate:local
# Run the backend seed
curl -X POST http://localhost:8788/api/v1/_seedExpected responses:
200 OK— seed executed successfully.409 ConflictwithALREADY_SEEDED— the database already contains users (ok).404 Not Found— the_seedendpoint is not available. Check that the Worker is running in development mode (wrangler dev).
2b. Is DEPLOY_ENV correct?
If DEPLOY_ENV=prod, the seed is disabled — neither frontend nor backend populates demo data. Check:
# In the frontend: app/.env or app/.env.local
# MUST NOT contain:
DEPLOY_ENV=prod
# Safe default for dev:
# (leave the variable absent — default is "dev")2c. Has the seed password been changed?
If the SEED_PASSWORD variable is set in .dev.vars, the default password senha123 will not work. Use the password defined in the variable, or remove SEED_PASSWORD from .dev.vars and run the seed again.
2d. Check users in D1
npx wrangler d1 execute neemias-db --local --command "SELECT email, role, status FROM users"If the table is empty, run the seed.
Quick solution (full dev environment reset)
# Remove local D1 and recreate
rm -rf .wrangler/state
pnpm db:migrate:local
curl -X POST http://localhost:8788/api/v1/_seed
# Clear frontend IndexedDB (via DevTools) and reload3. Sync stuck on "Syncing..."
Symptom
The interface shows "Syncing..." indefinitely, or events created offline never appear as synced.
Checks
3a. Is the backend accessible?
The frontend needs to know the backend URL. Check the app/.env file (or .env.local):
# Must contain:
VITE_BACKEND_URL=http://localhost:8788If the file does not exist, create it:
echo 'VITE_BACKEND_URL=http://localhost:8788' > app/.envRestart the frontend (pnpm dev) after creating or changing .env.
3b. Is the Worker running?
# Separate terminal:
pnpm dev:worker
# or:
cd workers && npx wrangler devThe Worker should respond at http://localhost:8788. Test:
curl http://localhost:8788/api/v1/health3c. Has the access token expired?
If the frontend was logged in for more than 15 minutes and the Worker was restarted (losing sessions), the refresh may fail. Log out and log in again.
3d. No-backend mode (fully offline)
If you do not need the backend and want to operate fully offline, use the "Mark as synced" button in Settings. This forces all events in the sync queue to the SYNCED state without communicating with the backend, allowing normal operation to continue.
Developing without a backend
Leave VITE_BACKEND_URL empty or remove the variable from .env:
# app/.env.local
VITE_BACKEND_URL=In this mode, the frontend operates with local authentication (no backend password validation) and all events are automatically marked as synced.
4. "SENSITIVE_DATA_LOCKED" error
Symptom
In the browser console, the error SENSITIVE_DATA_LOCKED appears when trying to create, edit, or delete a student. The operation fails silently or the interface shows an inconsistent state.
Cause
Sensitive data (changePayload, justifications) must be encrypted with AES-GCM before being saved to IndexedDB, and the encryption key is derived from the user's password during login. This key is kept only in memory (Map<userId, KeyContext>).
The SENSITIVE_DATA_LOCKED error means the key for the current user is not in memory. This happens when:
- The page was reloaded (F5) and the session has not finished hydrating —
AuthContextis in the middle of thehydrate()process. - The session expired (>15 min) and the refresh token failed — the user was disconnected.
- There was a race condition: the write operation was fired before
unlockSensitiveData()completed.
There is also a known edge case where session hydration (hydrate()) races with initial page rendering, causing a window where AuthContext.isAuthenticated is true but KeyContext has not yet been populated. This issue is documented in lesson L-002 of STATE.md.
Solution
- Log out and log in again. This forces a new key derivation and recreates
KeyContextfrom scratch. - Wait for the interface to fully load before interacting — the loading indicator should disappear.
- If the problem persists, clear IndexedDB (see Problem 1) and log in again.
5. Google Maps Autocomplete does not load
Symptom
The address field does not show Google Maps suggestions. The console displays Google Maps JavaScript API error: RefererNotAllowedMapError or similar.
Checks
5a. Is the API key configured?
# app/.env.local must contain:
VITE_GOOGLE_MAPS_API_KEY=your_key_hereThe key is loaded in app/src/lib/googleMaps.ts via import.meta.env.VITE_GOOGLE_MAPS_API_KEY. Without it, the Google Maps script is not loaded and autocomplete simply does not appear (silent failure).
5b. HTTP referrer restrictions
In the Google Cloud Console, check the key restrictions:
- For local development, add
localhostandlocalhost:5173(or the port you are using) to the allowed referrers list. - For staging/production, add the Cloudflare Pages domain (e.g.,
neemias.app,staging.neemias.app).
Format in the "HTTP referrers" field:
localhost
*.localhost
localhost:*
neemias.app
*.neemias.app5c. Are the APIs enabled?
In the Google Cloud Console, check that the following APIs are enabled for the project:
- Places API
- Maps JavaScript API
6. Build fails with "Cannot find module"
Symptom
When running pnpm build:app or pnpm build:worker, the build fails with a module not found error:
Error: Cannot find module '@neemias/schemas'
Error: Cannot find module 'jose'Cause
Monorepo dependencies are not installed or node_modules is in an inconsistent state. The project uses pnpm workspaces — dependencies are linked via node_modules/.pnpm and symlinks.
Solution
# From the monorepo root:
pnpm installIf the problem persists, do a full cleanup:
# Remove node_modules and lockfile
rm -rf node_modules packages/*/node_modules app/node_modules workers/node_modules
rm -rf pnpm-lock.yaml
# Reinstall from scratch
pnpm install
pnpm -r buildAlso check that you are using the correct pnpm version (>=8.x):
pnpm --version
corepack enable # if using corepack7. Worker deploy fails
Symptom
npx wrangler deploy or ./scripts/deploy.sh fails with an authentication error:
Error: Failed to get account ID. Please provide an account_id in your wrangler.toml
Error: Authentication error: Unable to verify accountChecks
7a. Is the Cloudflare token configured?
Wrangler needs authentication. Check if you are logged in:
npx wrangler whoamiIf not logged in:
npx wrangler login7b. Is CLOUDFLARE_API_TOKEN configured (CI/CD)?
In CI environments (GitHub Actions), configure the token as an environment variable or secret:
# Local (.dev.vars — for testing only, do not commit):
CLOUDFLARE_API_TOKEN=your_token_here
# CI: configure as a secret in GitHub Actions
# Settings → Secrets and variables → Actions → CLOUDFLARE_API_TOKENTo create a token: Cloudflare Dashboard → Create Token → Use the "Edit Cloudflare Workers" template.
7c. account_id in wrangler.toml?
Check that wrangler.toml contains the correct account_id (or is being inferred by the token):
# workers/wrangler.toml
name = "neemias"
main = "src/index.ts"
compatibility_date = "2025-06-01"
# Optional if the token has access:
# account_id = "your_account_id"7d. D1 migrations before deploy?
If the Worker has been modified and requires new migrations:
# Apply migrations in the remote environment
npx wrangler d1 execute neemias-db --remote --file=migrations/0001_init.sql
npx wrangler d1 execute neemias-db --remote --file=migrations/0002_auth_sessions.sql
# ... etc.8. D1 returns "D1_ERROR: no such table"
Symptom
The Worker responds with error 500 and the log shows:
D1_ERROR: no such table: studentsCause
D1 migrations have not been applied to the local or remote database. The .wrangler/state file may be corrupted or may have been removed without reapplying migrations.
Solution
Local environment:
# Apply all migrations on local D1
pnpm db:migrate:local
# Run the seed
curl -X POST http://localhost:8788/api/v1/_seedRemote environment (production/staging):
# List pending migrations
npx wrangler d1 migrations list neemias-db --remote
# Apply migrations
npx wrangler d1 migrations apply neemias-db --remoteCaution: never run
--remotemigrations in production without testing locally first.
9. Tests fail with crypto.subtle unavailable
Symptom
When running pnpm test, tests involving encryptionService or password.ts fail with:
ReferenceError: crypto is not defined
TypeError: crypto.subtle is undefinedCause
Tests use Vitest with the node environment by default, but crypto.subtle (Web Crypto API) is not available in Node.js without experimental flags or polyfills. Frontend tests mock encryptionService (vi.mock), but if a test does not apply the mock, it will find the real crypto.subtle, which does not exist.
Solution
Ensure that all modules importing encryptionService in test files have the mock applied before the import:
// Correct — mock before import
vi.mock("../auth/encryptionService", () => ({
encryptSensitiveValue: vi.fn(async () => ({ ciphertext: "x", iv: "y", keyVersion: 1 })),
decryptSensitiveValue: vi.fn(),
sha256: vi.fn(async (input: string) => input + "-hashed"),
}));
import { myFunction } from "../myModule";Worker tests (workers/src/__tests__/) run in a workerd environment via vitest-environment-miniflare, which has crypto.subtle available — they do not need mocks for encryption.
10. Blank page after deploy on Cloudflare Pages
Symptom
After deploying to Cloudflare Pages, the page loads completely blank (no visible errors, or with MIME type error in the console).
Possible causes
- SPA routing: Cloudflare Pages is not configured to serve
index.htmlon all routes. Neemias uses React Router — all requests need to fall through toindex.html. - Incomplete build:
pnpm build:appdid not generate all files inapp/dist/. - Incorrect
DEPLOY_ENV: the build was done with an empty or incorrectDEPLOY_ENV, and the seed tried to run in production.
Solution
SPA routing:
Add a _routes.json file in the app/dist/ directory (or configure in the Cloudflare Pages dashboard) to redirect all routes to index.html:
{
"version": 1,
"include": ["/*"],
"exclude": ["/assets/*", "/favicon.ico"]
}Check the build:
pnpm build:app
ls app/dist/ # Should list index.html, assets/, etc.Check DEPLOY_ENV:
# In Cloudflare Pages Dashboard:
# Settings → Environment variables → Production
# DEPLOY_ENV = prod
# Or via CLI:
wrangler pages secret put DEPLOY_ENV --project-name neemias-prod
# (type: prod)Quick reference
| Problem | Most common cause | Immediate action |
|---|---|---|
| Initialization error | Corrupted IndexedDB | Delete neemias-db in DevTools |
| Invalid credentials | Seed not run on backend | curl -X POST localhost:8788/api/v1/_seed |
| Syncing... | Missing/wrong VITE_BACKEND_URL | Check app/.env |
| SENSITIVE_DATA_LOCKED | Expired session | Logout → Login |
| Google Maps not loading | API key or referrer | Check .env.local and Cloud Console |
| Cannot find module | pnpm install pending | pnpm install from root |
| Worker deploy fails | Wrangler authentication | wrangler whoami / wrangler login |
| D1_ERROR: no such table | Migrations not applied | pnpm db:migrate:local |
| Blank page (Pages) | SPA routing or build | Check _routes.json and DEPLOY_ENV |
Still not resolved?
- Check Worker logs in the Cloudflare dashboard.
- Check the browser console (F12) for detailed errors.
- Read STATE.md for known issues and lessons learned.
- Open an issue on GitHub with:
- Problem description
- Steps to reproduce
- Console and terminal logs
- Environment (
dev/staging/prod, with or without backend)