Skip to content
Discord Get Started

Troubleshooting

Terminal
# Last 20 runs (default)
db9 functions history my-func --db myapp
# Last 50 runs
db9 functions history my-func --db myapp -n 50
# Specific run details
db9 functions history my-func <run_id> --db myapp
# JSON output
db9 functions history my-func --db myapp --json
FieldDescription
idUnique run ID
statussucceeded, failed, or timed_out for a finished run
result_jsonReturn value (if succeeded; absent when the handler returns undefined)
error_codeError type (if failed or timed out)
error_messageError details (if failed or timed out); cut at 64 KiB, ending in [db9: error message truncated]
logs_tailConsole output of the run (absent when it printed nothing)
omitted_fieldsRun lists only: names result_json, error_message, or logs_tail when that field is over 4 KiB and was left out — read the single run for it
trigger_typeinvoke, cron, or api
started_atExecution start time
finished_atExecution end time
attemptRetry attempt number
version_idWhich version was executed
Terminal
db9 functions logs my-func <run_id> --db myapp

Logs capture the function’s console output without timestamps. Each call’s output is prefixed with [stdout] (console.log(), console.info()) or [stderr] (console.error(), console.warn()):

Output
[stdout] hello 1
[stderr] oops
[stderr] careful
[stdout] fyi

A run keeps only its most recent 64 KiB of output. Earlier output is replaced by a first line such as [db9: 53418 bytes of earlier logs dropped], and the first line kept after it can start mid-line. The same text is returned as logs_tail by db9 functions invoke --json and the API.

Error CodeCauseResolution
execution_errorFunction threw an exception or returned invalid result. A missing handler export also gives this code, with Entrypoint "handler" is not a functionCheck error_message and logs for the stack trace; export module.exports = { handler: ... }
execution_timeoutFunction exceeded timeout_ms; the run’s status is timed_out, with a message such as Function execution timed out after 1000msIncrease --timeout or optimize code
result_too_largeThe return value’s JSON is larger than 256 KiB (Function result exceeds 262144 bytes)Return less, and write large output to a table or to fs9

SQL errors from ctx.db.query() propagate as exceptions:

ErrorCause
permission denied for table XThe authenticated role lacks privileges. Run GRANT as admin.
relation "X" does not existTable not found. Check the table name and schema.
syntax error at or near "..."Invalid SQL. Check your query string.

The exception’s shape depends on the function’s result format. In the default sql-api format it is a plain Error with no code property; the SQLSTATE is at the end of the message, so match on the message:

Output
ERROR: relation "no_such_table" does not exist | sqlstate: 42P01
ERROR: duplicate key value violates unique constraint "items_name_key" | detail: Key (name)=(apple) already exists. | sqlstate: 23505

With --db-api pg it is node-postgres’s error: err.code holds the SQLSTATE ("42P01"), err.message is the bare message (relation "no_such_table" does not exist), and err.detail is set when the server sends a detail.

  1. Check the run status and error:

    Terminal
    db9 functions history my-func --db myapp -n 1 --json
  2. Check the logs:

    Terminal
    db9 functions logs my-func <run_id> --db myapp
  3. Common causes:

    • execution_error — unhandled exception. Check logs for the stack trace.
    • execution_timeout — increase with --timeout.
    • permission denied for table X — grant access: GRANT ALL ON TABLE X TO authenticated;

”Entrypoint handler is not a function”

Section titled “”Entrypoint handler is not a function””

Your code doesn’t export handler as a named property. The runtime expects:

JavaScript
// Correct
module.exports = { handler: async (input, ctx) => { ... } };
// Wrong — bare function declaration isn't exported
async function handler(input, ctx) { ... }
// Wrong — module.exports as function (not object with handler key)
module.exports = async function handler(input, ctx) { ... };

The runtime uses CommonJS, not ES modules. export default is a syntax error:

JavaScript
// Wrong — ES module syntax is not supported
export default async function handler(input, ctx) { ... }
// Correct — use CommonJS
module.exports = { handler: async (input, ctx) => { ... } };

“Unsupported runtime imports detected”

Section titled ““Unsupported runtime imports detected””

The CLI detected require() calls that would fail at runtime. This happens when you use:

  • import somePackage from "package-name" — external npm packages are not available
  • import { something } from "./other-file" — multi-file imports are not supported

Bundle your code with esbuild to resolve all imports at build time. See Bundling.

  • Ensure your TypeScript syntax is valid
  • Use import type { ... } (not import { ... }) for type imports
  • Do not use import or require for runtime dependencies

If fetch() throws EACCES: permission denied ... url not in allowlist, the target URL must be added to the network_allowlist via the REST API.

Functions run as the authenticated role. Tables created by admin need explicit grants:

SQL
-- Replace my_table with your own table name
GRANT ALL ON TABLE my_table TO authenticated;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO authenticated;
LimitationDetails
Single-file deploymentMulti-file imports are not supported. Bundle with esbuild for complex projects.
No runtime npm packagesrequire("axios") fails. Bundle dependencies at build time.
TypeScript is transpile-onlyNo type checking at deploy time. Types are stripped.
No persistent stateEach invocation is isolated. Use ctx.db or ctx.fs9 to persist data.
Network requires allowlistfetch() is blocked by default. Configure network_allowlist via REST API.
No local emulatorTest by deploying to your database. There is no local dev server.
No delete commandFunctions cannot be deleted via CLI. Contact support if needed.
Authenticated roleFunctions run as authenticated, not admin. Grant table access explicitly.
Bundle sizeLarge bundles may hit the proxy body-size limit (413 error). Keep under 5 MB.