Skip to main content

Troubleshoot Functions

Work from the symptom you can see. Most failures fall into one of three places: the code was rejected when you saved it, the execution failed when it ran, or the execution never happened at all.

Start with three checks#

  1. Is the function active? Execute matches active functions only, so an inactive function returns 404 exactly like a missing one.
  2. Did a run happen? Every execution creates an invocation record. No record means the function never started.
  3. Which version ran? Records store the version that executed. A function that behaves unexpectedly may be running code you have since changed.

A function will not save#

Code must define main(params, user_data, sdk_client)#

Validation searches for that literal text. It's a string match, not a parse, so annotations, renamed parameters, or different spacing fail even when the function is valid Python. Copy the signature line exactly; rename parameters inside the body if you prefer.

Function code failed the security scan#

The save-time scan found a blocked construct: an import outside the allowed modules, a dynamic-execution builtin such as eval or open, an attribute name that starts with _, or a direct database connection. The findings are under errors.code. Remove the construct, use a permitted module, or move the work to a proxy function.

Code compilation failed#

The source isn't valid Python, or uses a language feature the sandbox doesn't allow. Read the named error. If the syntax is valid Python, rewrite the construct using simpler statements.

webhook_url is required for PROXY mode#

A proxy function was saved without a destination. Supply a public https:// URL.

webhook_url must use https#

The URL uses another scheme. Use https.

webhook_url host is not allowed#

The host is localhost, ends in .local, .internal, or .localhost, or resolves to a loopback, private, link-local, or carrier-grade NAT address. Point the function at a publicly reachable endpoint.

Code is required for APP mode functions#

An app function was saved with no code. Add code, or change the execution mode.

Invalid CEL syntax#

A filter condition couldn't be parsed. Correct the expression; see Filter conditions.

A function saves but fails to run#

main must define a function#

main exists but isn't callable, or was set to None. Define main as a function at the top level of the module.

main() accepts N parameters but must accept exactly 3#

The runtime signature check found the wrong number of parameters. Take exactly params, user_data, and sdk_client.

A NoneType attribute or call error naming the SDK#

A method was called on the wrong SDK attribute. Check the client path, such as sdk_client.database, for a misspelling.

The execution timed out#

Synchronous work exceeded the execution wait, 900 seconds by default, and the call returned 500. Execute asynchronously and collect the result by task ID.

Results are not what you expect#

An update returned 200 but nothing changed#

Versions are created only when a versioned field actually changes. A request matching the stored values returns a success status with an error body reading No changes detected, version unchanged. So does a request that changes only schedules or tags, which are saved anyway. Confirm the field and value differ, and read the body rather than the status code.

Reverting restored the code but not the configuration#

Revert restores code and filter_conditions only; other versioned fields keep their current values. Read the history and set the remaining fields yourself after reverting.

The version number went up after a revert#

Restoring code is itself a change, so the counter advances rather than rewinding. History is append-only.

The output doesn't match the source you're reading#

The run used an earlier version. Read the single invocation through the function-scoped endpoint, which returns the code that actually ran.

A proxy function's call failed, but the execute succeeded#

A non-2xx webhook response is recorded rather than raised, and a synchronous execute returns only the webhook's body. Read status_code and success from the run's task result, data.result from GET /api/result/TASK_ID/.

The log ends mid-run#

A limit was reached: 2,000 entries, 1 MiB in total, or 10,000 characters in one message. Entries past the limits are dropped, sometimes without a truncation entry. Log decisions and failures rather than every iteration.

A run never happened#

An event-triggered function that produces no invocation record at all was skipped by its filter conditions. A skipped execution dispatches no task and creates no record, so nothing marks the attempt.

A condition that errors counts as not matching. An expression that references a field the event doesn't carry, divides by zero, or uses an invalid regular expression resolves to false and skips the function without an error.

Check the expression against the event's actual parameters. An absent or empty expression permits execution, so a function that stopped running usually gained a condition rather than losing one. Filter conditions apply to event triggers only: if the function runs when called directly but not from an event, the condition is the cause.

Access is refused#

A 403 has two causes. The function is not public and the request carried no credentials, or policy denied an authenticated caller.

Send credentials first. If you are already authenticated, confirm your permission to execute that function. Do not set is_public to work around a permission problem — it makes the function executable by anyone, with no policy check at all. See Security and limits.

A 404 from execute means the app slug is wrong, the function slug is wrong, or the function is inactive. Slugs are generated from the name and may carry a numeric suffix when another function claimed the same one.

Functions are unavailable or limited#

When the organization's billing blocks Functions, execute calls are refused, with {detail, code, module} instead of the usual envelope. Both SDKs raise BillingError:

  • 402 account_suspended — The organization's account isn't active.
  • 429 product_suspended — The Functions usage for this billing period is used up (module: "functions").
  • 503 gate_unavailable — Billing status couldn't be read. Retry shortly.

Scheduled and event-triggered runs are refused when they reach a worker, so no result appears. Check the organization's billing state before investigating the function itself.

Prepare a support request#

Capture these, and confirm none carries a secret before sharing:

  • The app slug and function slug.
  • The celery_task_id of a failing run.
  • The invocation record, including its status and captured logs.
  • The version that ran, from the record's history id.
  • Whether the same call succeeds synchronously and fails asynchronously, or the reverse.