Skip to Content
DashboardsTroubleshooting

Troubleshooting

Find the symptom, then the cause. The server’s startup log answers most questions: it states the store, the sign-in method and where memberships come from.

The server does not start

Message or symptomCauseFix
Failed to start: error: database "…" does not existThe PostgreSQL URL points at a missing databaseCreate the database, or fix MIOT_DASHBOARD_POSTGRES_URL
Refuses MIOT_DASHBOARD_INSECURE_AUTHNODE_ENV=production, a non-loopback HOST, or JWT, ticket or proxy settings are also setUse a real identity provider outside local development
Refuses the credential keyShorter than 32 characters, or the store is memoryGenerate one with openssl rand -base64 32; use sqlite or postgres
Refuses the document storeThe database was first opened with a different MIOT_DASHBOARD_DOCUMENTSSet it back, or move the documents first
Refuses a lookup URLA GET URL lacks {userId}, {tenantId} or {scopeId}Add the placeholders
An error naming jose or pgAn optional package is missingnpm install jose or pg, or use the container image (it has jose)
ExperimentalWarning: SQLite is an experimental featureNormal on Node.js 22 and 24Nothing to fix
Fails with a generic module errorThe operations module could not loadCheck the path is absolute and the file exports createDashboardOperations()

Every request gets 403

CheckMeans
Startup log says 0 tenantsNo memberships loaded. Fix MIOT_DASHBOARD_SEED or the lookup URLs.
The user ID in the token differs from the membership fileMIOT_DASHBOARD_JWT_USER_CLAIM picks a different claim than the one you listed. Use the same value in both.
reason: TENANT_SCOPEThe person is not in that organization or scope, or the address is wrong
reason: CAPABILITYThey are a member, but their role does not allow the action
With ModularIoT, a direct call works nowhereExpected. Only calls through the modulith carry the proxy key.

401 Unauthenticated

CauseFix
No Authorization headerSend Authorization: Bearer <token>
Wrong issuer or audienceiss and aud in the token must match MIOT_DASHBOARD_JWT_ISSUER and _AUDIENCE exactly, including the trailing slash
Token expiredRefresh it. Clock drift above MIOT_DASHBOARD_JWT_CLOCK_TOLERANCE also counts as expired.
Wrong algorithmThe key source decides: JWKS or public key means RS256, a secret means HS256

Saving fails

AnswerCauseFix
409 CONFLICTSomeone saved since you loadedReload, compare, and apply your change again. Do not resend with the new revision blindly.
400 BAD_REQUESTThe document does not match the contractThe message names the field, such as preferences: Required
400 with versionThe document is not version 2Export it again from a current version
413The document is over 1 MiBRemove unused widgets or large staticData

In the web app, a failed save shows Unsaved changes and Reload. Your edits stay on screen.

A widget shows no data

Widget showsCauseFix
”This widget needs a saved query”It still uses a legacy data modeSet Data Source to Planner and choose a saved query
”Could not load the data”The query failedSee Queries fail
Loading foreverThe saved query’s result name does not match plannerVariableNameChoose the result again in the widget settings
An empty valueThe column name in the template is wrongUse the exact column name: {{completed}}
”Widget not available”The page has no registry for this componentIdAdd the registry, or replace the widget

Queries fail

Answer or symptomCauseFix
404 on every queryNo query executor configuredSet MIOT_DASHBOARD_OPERATIONS_MODULE (or _URL)
502 UPSTREAM_ERRORThe data service failed or returned something invalidCheck the next rows
Fails for one function only, with any filterA column holds a JSON objectPin select to scalar columns
Fails for one originIt is not in MIOT_DASHBOARD_DATA_ORIGINS, or it is not httpsAdd the exact origin
Fails after a whileOver 20 seconds, 5,000 rows or 2 MiBAggregate in the function or SQL, or add filters
BigQuery refuses to runThe dry run estimate is over maximumBytesBilled, or the statement is not a SELECTNarrow the query or raise the operation’s cap within the host ceiling
400 on a parameterThe value does not match the operation’s schema, or a required parameter is missingBind it, or give the operation a fixed value

The editor’s connection list is empty

MessageCauseFix
”The approved query catalog is unavailable or access was denied”No catalog provider, or no edit rightsSet MIOT_DASHBOARD_CATALOG_URL (ModularIoT) or queryCatalog, and check the role
The list loads but is emptyThe organization has no active connection with eligible operationsImport functions on a tested connection

The browser cannot reach the server

SymptomFix
CORS error in the consoleSet MIOT_DASHBOARD_CORS_ORIGINS to the page’s exact origin, with scheme and port
Preflight rejects a headerAdd it to MIOT_DASHBOARD_CORS_HEADERS
Page blocked by Content Security PolicyText widgets need 'unsafe-eval' in script-src

In the ModularIoT web app

SymptomFix
Dashboards is missing from the navigationSet ENABLE_DASHBOARD_SERVER=true on the web app
”Could not load dashboards. Check your organization and access.”The modulith cannot reach the dashboard server, or the proxy key differs. Check MIOT_DASHBOARD_BASE_URL and both keys.
No Create dashboard buttonYour role is Consumer, or the capability check failed
The modulith pod does not start after a deployThe proxy key secret is missing. Create it before deploying.

Still stuck

Open an issue at github.com/microboxlabs/modulariot  with the server version, the startup log lines, the request, and the full error answer. Remove tokens and keys first.

Last updated on