Error reference
When a run fails, Turbyn sets two output fields: errorCode (stable, safe to branch on) and errorMessage (human-readable, shown in HubSpot’s workflow history).
errorCode is the one to build logic against. Messages get reworded; codes don’t.
The codes
Section titled “The codes”| Code | Cause |
|---|---|
TIMEOUT |
The run exceeded your plan’s maximum runtime |
OUT_OF_MEMORY |
The run exceeded its memory limit |
RUNTIME_ERROR |
Your code threw an uncaught error |
ACTION_NOT_FOUND |
The selected saved action is missing or unpublished |
UNKNOWN |
Anything else |
TIMEOUT
Section titled “TIMEOUT”The run hit your plan’s ceiling. That is minutes rather than HubSpot’s 20 seconds, but it is still finite.
Usual causes: a third-party API that hung with no timeout of its own, an unbounded pagination loop, or a job that genuinely needs more time than your plan allows.
Always set a timeout on outbound calls. Without one, a hanging provider consumes your whole budget:
const res = await fetch(url, { signal: AbortSignal.timeout(30_000) });OUT_OF_MEMORY
Section titled “OUT_OF_MEMORY”The run exceeded its memory limit. Almost always from holding a large dataset in memory at once. A big API response, a large CSV, unbounded accumulation in a loop.
Stream rather than buffer where you can, and process in batches rather than loading everything:
// Accumulates every record before doing anything.const all = await fetchAllRecords();all.forEach(process);
// Bounded. Only one page is resident at a time.for await (const page of fetchPages()) { page.forEach(process);}RUNTIME_ERROR
Section titled “RUNTIME_ERROR”Your code threw and didn’t catch it. errorMessage carries the thrown message; the full stack trace is on the run detail page in Turbyn.
This is also what a deliberate throw produces, which makes it a legitimate way to fail a record on purpose:
if (!event.inputFields.domain) { throw new Error("No domain on this record. Nothing to enrich");}Since that message surfaces in the workflow history, write it for the person reading the workflow.
ACTION_NOT_FOUND
Section titled “ACTION_NOT_FOUND”The workflow step points at a saved action that can’t be resolved. Either it was deleted, or it was never published, or the step was configured against a different portal.
Check that the action still exists and has a published version. Actions that only exist as drafts don’t appear in HubSpot’s dropdown and can’t be executed.
UNKNOWN
Section titled “UNKNOWN”An infrastructure-level failure that didn’t map to a specific code, such as a sandbox that failed to start. These are ours, not yours. If one recurs for the same action, the run detail page has the diagnostic information worth sending us.
Branching on errors in HubSpot
Section titled “Branching on errors in HubSpot”The Run Code action declares execution rules on errorCode, so HubSpot’s workflow editor gives you branches per failure type without any configuration:
Run Code ├─ Success → continue ├─ Timed out → notify ops, retry later ├─ Runtime error → create a task for the data team └─ Not found → alert an adminEach rule renders a readable message on the workflow history page, with {{errorMessage}} interpolated.
Handling failure without failing
Section titled “Handling failure without failing”Often you don’t want a failure to stop the record’s journey. Return a status instead of throwing:
try { const data = await enrich(domain); return { outputFields: { output1: "enriched", output2: data.industry }, };} catch (err) { ctx.log.warn("enrichment failed", { domain, error: err.message }); return { outputFields: { output1: "skipped", output2: err.message }, };}Now the workflow branches on output1 and the record keeps moving. A thrown error stops that enrollment; a returned status lets you decide what happens next.
Retries and replay
Section titled “Retries and replay”Failed runs are recorded with their full input payload, so they can be replayed from the Runs list. Replay one, or filter and replay in bulk (“all failures in the last 24 hours”).
Replay uses the version the original run executed, so you can reproduce a historical failure faithfully. Replay against your current draft when you’re testing a fix.