Skip to content

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

  1. Open Chrome DevTools (F12).
  2. Go to the Application tab → StorageIndexedDB.
  3. Locate the neemias-db database.
  4. Right-click → Delete database.
  5. Close all other tabs on the same domain (localhost:5173 or the deploy domain) before reloading.
  6. 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:

js
// 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_ENV is not prod), 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.

bash
# Check if migrations have been applied
pnpm db:migrate:local

# Run the backend seed
curl -X POST http://localhost:8788/api/v1/_seed

Expected responses:

  • 200 OK — seed executed successfully.
  • 409 Conflict with ALREADY_SEEDED — the database already contains users (ok).
  • 404 Not Found — the _seed endpoint 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:

bash
# 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

bash
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)

bash
# 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 reload

3. 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):

bash
# Must contain:
VITE_BACKEND_URL=http://localhost:8788

If the file does not exist, create it:

bash
echo 'VITE_BACKEND_URL=http://localhost:8788' > app/.env

Restart the frontend (pnpm dev) after creating or changing .env.

3b. Is the Worker running?

bash
# Separate terminal:
pnpm dev:worker
# or:
cd workers && npx wrangler dev

The Worker should respond at http://localhost:8788. Test:

bash
curl http://localhost:8788/api/v1/health

3c. 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:

bash
# 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:

  1. The page was reloaded (F5) and the session has not finished hydrating — AuthContext is in the middle of the hydrate() process.
  2. The session expired (>15 min) and the refresh token failed — the user was disconnected.
  3. 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

  1. Log out and log in again. This forces a new key derivation and recreates KeyContext from scratch.
  2. Wait for the interface to fully load before interacting — the loading indicator should disappear.
  3. 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?

bash
# app/.env.local must contain:
VITE_GOOGLE_MAPS_API_KEY=your_key_here

The 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 localhost and localhost: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.app

5c. 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

bash
# From the monorepo root:
pnpm install

If the problem persists, do a full cleanup:

bash
# 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 build

Also check that you are using the correct pnpm version (>=8.x):

bash
pnpm --version
corepack enable  # if using corepack

7. 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 account

Checks

7a. Is the Cloudflare token configured?

Wrangler needs authentication. Check if you are logged in:

bash
npx wrangler whoami

If not logged in:

bash
npx wrangler login

7b. Is CLOUDFLARE_API_TOKEN configured (CI/CD)?

In CI environments (GitHub Actions), configure the token as an environment variable or secret:

bash
# 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_TOKEN

To 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):

toml
# 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:

bash
# 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: students

Cause

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:

bash
# Apply all migrations on local D1
pnpm db:migrate:local

# Run the seed
curl -X POST http://localhost:8788/api/v1/_seed

Remote environment (production/staging):

bash
# List pending migrations
npx wrangler d1 migrations list neemias-db --remote

# Apply migrations
npx wrangler d1 migrations apply neemias-db --remote

Caution: never run --remote migrations 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 undefined

Cause

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:

ts
// 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

  1. SPA routing: Cloudflare Pages is not configured to serve index.html on all routes. Neemias uses React Router — all requests need to fall through to index.html.
  2. Incomplete build: pnpm build:app did not generate all files in app/dist/.
  3. Incorrect DEPLOY_ENV: the build was done with an empty or incorrect DEPLOY_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:

json
{
  "version": 1,
  "include": ["/*"],
  "exclude": ["/assets/*", "/favicon.ico"]
}

Check the build:

bash
pnpm build:app
ls app/dist/                    # Should list index.html, assets/, etc.

Check DEPLOY_ENV:

bash
# 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

ProblemMost common causeImmediate action
Initialization errorCorrupted IndexedDBDelete neemias-db in DevTools
Invalid credentialsSeed not run on backendcurl -X POST localhost:8788/api/v1/_seed
Syncing...Missing/wrong VITE_BACKEND_URLCheck app/.env
SENSITIVE_DATA_LOCKEDExpired sessionLogout → Login
Google Maps not loadingAPI key or referrerCheck .env.local and Cloud Console
Cannot find modulepnpm install pendingpnpm install from root
Worker deploy failsWrangler authenticationwrangler whoami / wrangler login
D1_ERROR: no such tableMigrations not appliedpnpm db:migrate:local
Blank page (Pages)SPA routing or buildCheck _routes.json and DEPLOY_ENV

Still not resolved?

  1. Check Worker logs in the Cloudflare dashboard.
  2. Check the browser console (F12) for detailed errors.
  3. Read STATE.md for known issues and lessons learned.
  4. Open an issue on GitHub with:
    • Problem description
    • Steps to reproduce
    • Console and terminal logs
    • Environment (dev/staging/prod, with or without backend)

Distributed under MIT License.