Troubleshooting¶
Common problems and how to fix them, grouped by area.
Builds and deploys¶
The Pages build fails. Open the failed deployment's build log in the Pages dashboard and read the first red error.
- Make sure Build command is
npm run buildand Build output directory isclient/dist. - A
tsc/ TypeScript error means the code doesn't compile — runnpm run buildlocally to reproduce and fix it, then push again.
The site loads but shows a blank page or 404s on refresh.
Confirm Build output directory is client/dist. Pages serves the single-page app from there.
Database¶
Screens error or show no data; the API returns 500. The remote database probably has no tables. Run:
wrangler says it can't find the database / wrong ID.
Check that database_id in wrangler.toml matches an existing database. List them with npx wrangler d1 list. Re-run npx wrangler login if you're not authenticated.
A new feature errors about a missing column.
A migration wasn't applied. Run npm run db:migrate:remote (and :local for your machine). For example, the "who changed it" history feature needs migration 0010_snapshot_created_by.sql.
Local and remote behave differently.
They're separate databases. Apply migrations to both (db:migrate:local and db:migrate:remote). Never edit an already-applied migration file — add a new numbered one.
Sign-in and roles¶
Visiting the app doesn't ask me to sign in.
The Cloudflare Access policy isn't covering the domain. In Zero Trust → Access → Applications, confirm an application exists for oncall.totlcom.com with an Allow policy. Test in a private window.
/api/v1/me shows email: "unknown".
Cloudflare Access isn't in front of that request, or the token isn't reaching the app. Confirm the Access application's domain exactly matches oncall.totlcom.com and that you reached the app through the sign-in (not a preview *.pages.dev URL that isn't covered by the policy).
No one is an admin / the Admin tab is missing.
ADMIN_EMAILS isn't set in production, or doesn't include your address. Set it in Pages → Settings → Variables and secrets (Environment variables), then redeploy. Your email there must match your Microsoft sign-in email exactly.
Microsoft sign-in fails or loops.
- The client secret may have expired — create a new one in Entra ID and update it in Zero Trust → Settings → Authentication.
- Check the Redirect URI in the Entra app exactly matches https://<team>.cloudflareaccess.com/cdn-cgi/access/callback.
- Make sure admin consent was granted for the API permissions.
A user can sign in but shouldn't be able to. Access (who can sign in) is separate from app roles (what they can do). Remove them from the Access policy to block sign-in entirely; set them to Viewer in the Admin tab to merely remove editing.
Docs site¶
mkdocs: command not found.
Install the packages first: pip install -r requirements.txt from the docs-site folder. On macOS try pip3.
Docs site shows "Not Found" after upload.
You uploaded the site folder instead of its contents. Re-upload so index.html is at the top level. See Deploy this docs site.
Docs don't ask for sign-in.
Add a Cloudflare Access application + policy for docs.oncall.totlcom.com (Part 4).
Still stuck?¶
Capture the exact error text (and the build log if it's a deploy issue) before asking for help — it almost always points straight at the cause.
ConnectWise¶
"ConnectWise rejected the credentials (401)" — the company ID, public key or private key is wrong. The company ID is the short one you type at the ConnectWise login screen, not the company's display name.
"Missing or invalid clientId" — the clientId wasn't created at developer.connectwise.com, or was pasted with stray whitespace.
Connection works but reports 0 members — the API member's security role is missing System → Member Maintenance → Inquire.
"ConnectWise refused the schedule entry (403)" — the role is missing Service Desk → Resource Scheduling. There is no top-level "Schedule" module; expand Service Desk to find it.
Entries get created but never removed — Resource Scheduling → Delete isn't set to All. This one is sneaky: it looks like the integration is working until the first reassignment, then duplicates start piling up.
"Members" is missing from your own System menu — that's a permission on your role, not a missing feature. Security Roles and Members are gated separately. Fix your own role's System → Member Maintenance, save, then sign out and back in — ConnectWise caches role permissions for the session.
"Stored ConnectWise credentials could not be decrypted" — CWM_ENC_KEY was changed while credentials were saved under the old one. The ConnectWise tab shows a red banner saying so. Re-enter the five values and test; see Rotating it on the environment variables page.
The scheduled Worker logs a decryption error but the app works fine — the Worker's CWM_ENC_KEY doesn't match the Pages one. They read the same stored credentials, so the values must be byte-identical. Re-run npm run cron:secret with the value the Pages project uses.
"The schedule changed since this plan was previewed" — somebody edited the schedule between your preview and your push. That's the guard working. Preview again and read the new diff.
A sync reports an entry "was created but not recorded" — ConnectWise made the hold but the app couldn't save its id, so nothing will ever clean it up. Delete that entry id by hand in ConnectWise. The id is also in the activity log.