Troubleshooting
Blueprint Validation Errors
Section titled “Blueprint Validation Errors”Blueprint validation failed: ...
Section titled “Blueprint validation failed: ...”This appears in the terminal or as a browser overlay during hot reload. The message includes the field path and what was wrong.
Check the field path in the error. For example:
entity.Post.fields.2.type: Invalid enum value. Expected 'Text' | 'LongText' | ...means the third field of the Post entity has an unrecognized type.
Common causes:
- Misspelled or misnamed field type (e.g.
"string"instead of"Text") - Missing required
primary_key = trueon the ULID/UUID field - Enum values not listed in
values = [...]for anEnumfield refon aReffield pointing to an entity that doesn’t exist in the blueprint- TOML syntax error (an unclosed bracket or missing quote)
Run zebric validate --blueprint blueprint.toml to get the full error list before starting the server.
Schema field not found at runtime
Section titled “Schema field not found at runtime”If a page queries or displays a field that isn’t defined on the entity, the runtime will surface this as an error. Verify that every field referenced in [page."/path".query], form.fields, or detail.fields exists in the corresponding entity.
Dev Server & Hot Reload
Section titled “Dev Server & Hot Reload”Hot reload not triggering
Section titled “Hot reload not triggering”- Confirm
zebric devwas used (notzebric startor a direct node invocation without dev config) - Check that you’re editing the file referenced by
--blueprint - Some editors write files via a temp-then-rename sequence; this is supported, but check editor settings if changes aren’t picked up
- On Linux, you may hit the inotify watch limit. Increase it:
Terminal window echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.confsudo sysctl -p
Browser doesn’t reload after blueprint change
Section titled “Browser doesn’t reload after blueprint change”- The reload WebSocket only connects when you access via
localhostor127.0.0.1— it won’t inject on IP addresses or hostnames - Open the browser console and look for
[Zebric] Connected to live reload server— if it’s not there, check that port 3000 is reachable and no proxy is stripping WebSocket upgrades - If the blueprint has a validation error, the error overlay appears instead of a reload. Fix the error in the file, and save again.
Admin server not responding on port 3030
Section titled “Admin server not responding on port 3030”- The admin server only starts in dev mode. If you started with a production config (no
devblock), it won’t run. - Confirm nothing else is bound to port 3030. Override with
adminPortin the engine config if needed.
Authentication
Section titled “Authentication”Redirect loop on a protected page
Section titled “Redirect loop on a protected page”- Verify the page has
auth.required = trueand that an auth provider is configured in[auth] - The login page must be accessible without authentication. Check that
/login(or your configured path) does not itself require auth.
403 Forbidden on an API request
Section titled “403 Forbidden on an API request”- For browser requests, check that the user is logged in (session cookie present)
- For API key requests, verify the
Authorization: Bearer <key>header is set and matches thekeyEnvvalue in[[auth.apiKeys]] - Check entity-level access control:
[entity.Foo.access]rules are evaluated against$currentUser. A missing session produces no$currentUser, causing all access checks to fail.
Session not persisting between requests
Section titled “Session not persisting between requests”- Session cookies require HTTPS in production. In local development, use
http://localhost(not an IP or custom hostname) to avoid cookie rejection. - Verify
SESSION_SECRETis set in your environment — if it’s undefined, sessions may not serialize correctly.
Workflows
Section titled “Workflows”Workflow not triggering on entity change
Section titled “Workflow not triggering on entity change”- Check the blueprint: the
entityandeventvalues must exactly match the entity name and event type (create,update,delete). - For conditional triggers, both
before.*andafter.*conditions must be met. Use the admin server’s/tracesendpoint to see whether the workflow span appears at all. - If the workflow appears in
/workflowsathttp://127.0.0.1:3030/workflowsbut never fires, add aconsolenotification adapter to get execution logs:[[notifications.adapters]]name = "debug"type = "console"
Workflow step failed: template variable not resolved
Section titled “Workflow step failed: template variable not resolved”All {{ variable.path }} expressions are resolved at execution time. If the path doesn’t exist in the workflow context, the step fails.
- For manual triggers: use
variables.data.record.*for the triggering record andvariables.data.payload.*for action bar payload data - For entity event triggers: use
trigger.before.*andtrigger.after.* - For webhook triggers: use
webhook.body.*,webhook.headers.*,webhook.query.*
Check the template variable reference.
Workflow job stuck in queue
Section titled “Workflow job stuck in queue”- The default
maxConcurrentis 10. If all slots are occupied by long-running jobs, new jobs wait. - Jobs are in-memory only. A server restart clears all pending and running jobs.
Webhook step blocked
Section titled “Webhook step blocked”- In production, the HTTP client blocks requests to private IPs and
localhost(SSRF protection). Use publicly routable URLs for webhook destinations. - In development, private IPs are allowed. Make sure you’re running in dev mode when testing local endpoints.
File Uploads
Section titled “File Uploads”Form not accepting files
Section titled “Form not accepting files”- Confirm the field has
type = "file"in the blueprint (not"Text"or"File") - The runtime sets
enctype="multipart/form-data"automatically — if you’re using a custom form outside the blueprint, you must set it manually
File too large error
Section titled “File too large error”- The per-field
maxattribute is in bytes. 10 MB =10485760. - The global server limit is 50 MB regardless of the field’s
maxvalue.
Invalid file type error
Section titled “Invalid file type error”- The
acceptarray uses MIME types, not file extensions."pdf"will not work; use"application/pdf". - The browser may show the wrong MIME type for some files. Server-side validation uses the MIME type reported by the browser — if the browser gets it wrong, the check fails.
Uploaded file returns 404
Section titled “Uploaded file returns 404”- Files are served from
/uploads/{stored-filename}, not the original filename. - Use the value stored in the entity’s
attachmentfield (the URL) to build links, not the user-supplied filename. - Check that
./data/uploads/exists and is writable by the process.
Performance
Section titled “Performance”Slow list pages
Section titled “Slow list pages”- Large tables without pagination will load all rows. Add
limitto your page query:[page."/posts".query]limit = 25 - Check
/traces/slowon the admin server to identify which database query is the bottleneck.
High p95 latency in /traces/stats
Section titled “High p95 latency in /traces/stats”Drill into slow traces:
curl "http://127.0.0.1:3030/traces/slow?threshold=500" | jq '[.[] | {path: .metadata.path, ms: .duration, spans: [.spans[] | {name: .name, ms: .duration}]}]'Look for spans with disproportionate duration — usually a database.query span indicates a missing index or a full-table scan.
Schema Migrations
Section titled “Schema Migrations”Column not found error after adding a field
Section titled “Column not found error after adding a field”After adding a field to an entity, the database schema needs to be updated. In development, restart with --seed. In production, check /schema-diff on the admin server and run a manual migration.
Breaking change warning in schema-diff
Section titled “Breaking change warning in schema-diff”Removing or changing the type of an existing field is a breaking change. The admin server will flag it:
{ "hasBreakingChanges": true, "fieldsChanged": [...] }In production, plan the migration: add the new field first, backfill data, then remove the old field in a subsequent deploy. Never drop columns live without verifying no queries reference them.