Test and monitor functions

Run test invocations against the real sandbox, read the execution log, and diagnose failing or silent functions.

6 min read

Both tools are in the function editor: test a function before you wire it up, then read its log once it runs for real.

Run a test invocation

Testing requires status Ready: a function that has not built successfully has nothing to run.

On the Functions tab:

  1. On the function's row, select Edit.

    Functions tab with Edit on Signup email normalizer highlighted

  2. Select the Test tab.

    Function editor with the Test tab highlighted

  3. Under Load example, choose a sample input.

    Test tab with Load example set to Contact create (pre), highlighted

    The input editor fills with a payload matching the function's kind: a full event envelope for any event type in the catalogue, or the typed input for the hook point.

  4. (Optional) Edit the payload, then select Beautify JSON to reformat it.

    Test tab with the Beautify JSON button highlighted

    The editor validates JSON as you type and marks the offending line if it can't parse.

  5. Select Run test.

    Test tab with the Run test button highlighted

    You can leave Timeout override (ms) empty: a test run always uses the function's own configured timeout.

Test result with a continue decision and the captured logs

What you see depends on the kind:

  • Platform event functions show an exit code badge, the duration, the request id, and the output. Exit code 0 with no error means success. The output is shown for debugging only: in production it is discarded, so a function that needs an effect must act through the SDK.
  • Hook shows the decision your handler returned — continue or deny — plus the code and message on a denial, and the result object it supplied.

Anything your handler logged with console.log and friends appears in a Logs panel below the result, alongside a couple of platform lines showing the runtime starting up and the handler being called.

A test invocation is a real invocation

Tests run in the real sandbox with the real SDK. An allow-listed HTTP request genuinely goes out, and a contact custom field write genuinely lands. Use test data, and be careful with hooks that call an external provider — a contact.verify test really sends an email.

Reading a hook contract violation

When a hook run fails, the result shows a Contract violation panel instead of a decision. In production the same run fails the operation.

It appears when your handler throws, returns nothing, uses a decision other than continue or deny, or returns a result field the hook point doesn't define. A plain { decision: "continue" } object is fine, and a deny with an empty code is reported as DENIED.

In the execution log, a malformed answer still reads exit code 0, because the run itself succeeded. When a hook blocks operations but its runs look clean, paste a recorded input here and run it.

Read the execution log

The Executions tab lists real invocations, newest first, and is the primary tool for answering "did it run, and what happened".

ColumnWhat it tells you
StartedWhen the invocation began
SourceHow it was triggered — event, hook, or test
Exit code0 for success; anything else, or a populated error, means the run failed
DurationEnd-to-end time for the invocation, so it can read much higher than the handler time shown on a test result
Error / Request idThe failure message, or the request id to quote to support

Records are kept for 60 days and then expire, so capture anything you need for a post-mortem before then. Keep each version of your handler in your own repository too — the platform tracks a version number and the compiled artifact, not your source history, so your repo is what lets you put the previous version back. The list loads 20 at a time with Load more, and Refresh pulls the newest. While more remain, the count beside Executions reads 20+, then 40+; once all are loaded, it shows the exact total.

Select a row to open its detail, which adds everything the platform recorded:

  • The input and output as stored, capped at 256 KB per side. When either was longer, a banner reports the original sizes.
  • The WASM sha — which compiled version actually ran. This is how you tell "my new code is broken" from "the old version is still serving".
  • The full logs, or a truncated marker when your handler logged more than the capture allows.
  • Trace metrics under a collapsible section: how many SDK calls the run made, memory used, and a breakdown of where the time went — waiting to start, getting ready to run, and running your code.

Executions tab listing recent invocations, with the 60-day retention note highlighted

If a function feels slow, check the trace metrics before touching your code. The time OmniLab spends preparing the first run after a change is not something your handler can influence, and it does not count against your timeout. Only the time attributed to your own code is yours to improve.

If something's wrong

Still stuck?

Send support the request id from the failing execution and the build id from the Build tab. Execution records expire after 60 days, so capture the detail view before then — including the input, the error, and the trace metrics.

Next steps

On this page