# Debugging and logs

> Find out why a workflow did or didn't run. Covers live logs in the builder, failed-run records from the processes API, reading their context, the most common error messages, and a checklist for workflows that silently do nothing.

Source: https://developer.kustomer.com/docs/workflows/debugging

A workflow that misbehaves either **fails** (a step throws, and Kustomer records the error) or **does nothing** (no error, but no effect either). Failures are easy to find. Doing nothing usually means a condition didn't match, and the logs show you which one. For how a run moves from step to step, see [How workflows run](https://developer.kustomer.com/docs/workflows/how-workflows-run).

| Tool                                                                                                                   | Shows                                                                    | Retention                                                                             |
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| **View Logs** in the builder                                                                                           | Every run, step by step, with each step's input and output               | Records for one hour after you turn on **Debug Mode**. Shows the 100 most recent runs |
| **View errors** in the builder, or **View Error Logs** on the Workflows list                                           | Failed runs: "Failed with Error at step …". System retries aren't listed | Recent failures. **Clear All Errors** empties the list                                |
| [`GET /v1/workflows/{id}/processes`](https://developer.kustomer.com/api/workflows/get-processes-errors-by-workflow)    | Failed runs, with the triggering event and the full context              | Paginated history                                                                     |
| `lastError` on [`GET /v1/workflows/{id}`](https://developer.kustomer.com/api/workflows/get-a-workflow-based-on-the-id) | The most recent failure                                                  | Latest only                                                                           |
| [Inbound hook debug logs](https://developer.kustomer.com/docs/events/inbound-webhooks#debug-a-hook)                    | What a hook received                                                     | 24 hours                                                                              |

## Watch runs live

#### Open the logger

Open the workflow and select **View Logs** to open **Workflow Log Events**. Turn on **Debug Mode**. Kustomer records the workflow's runs for the next hour, which sets `logging: true` on the workflow.

#### Cause the trigger

Do the thing that should start the workflow, such as updating a test conversation. Remember that a workflow you just saved can take about a minute to start matching.

#### Refresh and inspect

Refresh the logger. Each run is listed as a log event with when it completed, how long it took and its last step. Open a run to see every logged step, then select a step to see its input and output. If nothing ran, the logger shows "No applicable logs".

_Figure: The workflow builder with the View Logs panel open. The left side lists recent runs with green and red status dots. The selected run shows the trigger and two action steps, and the right panel shows the selected step's input and output JSON. Each step's output is exactly what /#steps.\<id> refers to._

A step's output in the logger is what `/#steps.<id>` resolves to in later steps. If a param is coming out empty, compare its path with the output of the step it references. A misspelled field or the wrong step ID is the most common cause. Remember that the numbers on the canvas are positions, not step IDs.

Each logged run records, for every step, its input, its output, whether it succeeded and when it ran, plus the workflow version that ran. Secrets that Kustomer adds to a step's input are removed from the log. A param that's missing from a step's input usually means its reference resolved to `null` or `undefined`, so the param wasn't sent.

## Read failed runs with the API

Every failed run is stored as a _process_. List a workflow's failures, newest first, optionally filtered by date:

```bash
kusty api GET /v1/workflows/$WORKFLOW_ID/processes --query createdAtFrom=2026-10-20T00:00:00Z --query pageSize=20 --output json
```

```json title="One failed run (abridged)"
{
  "type": "workflowProcess",
  "id": "5eea124bccb9de001933f317",
  "attributes": {
    "status": "failed",
    "createdAt": "2026-10-21T12:53:30.684Z",
    "error": "{\"message\":\"Invalid regular expression: /*/: Nothing to repeat\",\"step\":\"Se73DhAs5\"}",
    "event": { "name": "kustomer.customer.update", "data": { "type": "customer", "id": "5e83c3a4d95b7e0012dc9711", "attributes": { … } } },
    "meta": {
      "context": "{\"order\":[\"1\",\"tNG_FZugW\",\"Se73DhAs5\"],\"steps\":{\"1\":{\"name\":\"kustomer.customer.update\",\"output\":{…}},…}}",
      "attempt": 1,
      "retry": false,
      "workflowRev": 27
    }
  }
}
```

**Process attributes**

`error` and `meta.context` are strings, so parse them before reading. This script prints a one-line summary per failure:

```ts title="failures.ts"
const base = 'https://acme.api.kustomerapp.com'
const res = await fetch(`${base}/v1/workflows/${process.env.WORKFLOW_ID}/processes?pageSize=50`, {
  headers: { Authorization: `Bearer ${process.env.KUSTOMER_API_KEY}` },
})
const { data } = await res.json()

for (const p of data) {
  const error = JSON.parse(p.attributes.error ?? '{}')
  const context = JSON.parse(p.attributes.meta?.context ?? '{}')
  console.log(
    p.attributes.createdAt,
    `rev ${p.attributes.meta?.workflowRev}`,
    `${p.attributes.event?.name} ${p.attributes.event?.data?.id}`,
    `path ${(context.order ?? []).join(' → ')}`,
    `failed at ${error.step}: ${error.message}`,
  )
}
```

```bash title="jq"
kusty api GET /v1/workflows/$WORKFLOW_ID/processes --output json \
  | jq -r '.data[].attributes | [.createdAt, (.error | fromjson | "\(.step): \(.message)"), (.meta.context | fromjson | .order | join(" → "))] | @tsv'
```

## Common errors

| Error message                                                                                        | Cause and fix                                                                                                                                                                                                                                                                                             |
| ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `next step not found`                                                                                | A transition's `target` doesn't match any step `id`. Check for renamed or deleted steps                                                                                                                                                                                                                   |
| `operation <op> not supported`                                                                       | A condition uses an `op` that doesn't exist, such as `in`. See [operators](https://developer.kustomer.com/docs/workflows/conditions-and-expressions#operators)                                                                                                                                            |
| `trigger <eventName> not found`                                                                      | The trigger's event isn't available, for example because the app that provides it was uninstalled                                                                                                                                                                                                         |
| `Missing param uri` or `Missing param method`                                                        | A REST step's URL or method resolved to nothing. Usually a reference to a field that doesn't exist                                                                                                                                                                                                        |
| `Missing param regex` or `Missing param testString`                                                  | A regex step's pattern or input resolved to nothing                                                                                                                                                                                                                                                       |
| `runAt type must be one of absolute or relative`, `invalid runAt value`                              | Fix the Schedule a Workflow step's `runAt`. See [Schedule a workflow](https://developer.kustomer.com/docs/workflows/triggers-and-actions#schedule-a-workflow)                                                                                                                                             |
| `Payload too large to schedule workflow`                                                             | The scheduled workflow's `inputs` are too big. Pass IDs instead of whole records                                                                                                                                                                                                                          |
| `Method must be one of post, get, patch, put, or delete`                                             | Fix the REST step's `method`                                                                                                                                                                                                                                                                              |
| `Invalid url protocol`, `DNS failure for …`, `Bad IP address for …`, `IPv6 hostname … not supported` | The URL breaks the [REST step's rules](https://developer.kustomer.com/docs/workflows/code-steps#where-you-can-send-requests): it isn't `http`/`https`, doesn't resolve, or points at a private or reserved address                                                                                        |
| `Unescaped characters in request URL`                                                                | A value with spaces or special characters went into a URL. Wrap it with `{{{uriEncode …}}}`                                                                                                                                                                                                               |
| `Invalid regular expression`                                                                         | Fix the regex step's pattern. Remember to escape backslashes in JSON (`"\\d+"`)                                                                                                                                                                                                                           |
| A Kustomer API error, such as a validation `title` and `detail`                                      | A Kustomer action rejected its input: a missing required field, the wrong type, or an ID that belongs to another kind of record. The request is sent even when a required input is missing or empty (`0`, `false` and `""` count as empty). Compare the step's input in the logs with the action's inputs |

REST steps that get an error _status_ from your service don't appear here: they succeed, and your transitions handle the status. See [Handle responses and failures](https://developer.kustomer.com/docs/workflows/code-steps#handle-responses-and-failures).

### Errors when you save

Some problems are caught when you save rather than when the workflow runs. The most common is a loop: `Step with ID <id> transitions to a previous step with ID <workflowId>:<target>`, shown on the builder's canvas as "Cycle detected". Others include a duplicate `name` (`409`), nested root `meta`, and duplicate transitions. The full list is in [Validate before you save](https://developer.kustomer.com/docs/workflows/workflow-json#validate-before-you-save).

## When something runs twice

A run can repeat work for two reasons:

- **A system retry.** When a Kustomer action gets a `5xx`, or the platform has a temporary problem, the run starts again from step `1`. Every step before the failure runs again, including creates, notes and REST calls. Make those steps safe to repeat: look up before you create, prefer **Add Tags** over replacing a tag list, and make your own endpoints idempotent. These retries don't appear in **View errors**.
- **The workflow triggered itself.** An update trigger fires again for the workflow's own change. Guard it with a condition on `changes`, as described in [Workflows](https://developer.kustomer.com/docs/workflows#the-life-of-a-run).

## When nothing happens

Work down this list. Most "it didn't run" reports stop at one of the first four items.

- [ ] **Enabled?** Check the toggle or `enabled` in the JSON.
- [ ] **Waited a minute?** New and changed workflows take about 60 seconds to start matching.
- [ ] **Right trigger?** A new message fires `kustomer.message.create` and also `kustomer.conversation.update`. Make sure you picked the event that actually happens.
- [ ] **First condition matched?** Open the run in View Logs. A run that stops after the trigger means no trigger transition matched. Look at step `1`'s output and compare it with the condition, paying attention to types (`"4"` vs `4`), IDs vs names (tags and users are IDs), and `changes` paths.
- [ ] **Branch order?** The first matching transition wins. A broad condition before a specific one swallows its runs. In the builder, that's the leftmost branch.
- [ ] **Empty values?** A param or condition that comes out empty often references a variable by name instead of ID (`{{{variables.apiKey}}}` renders nothing), uses a step's position number instead of its ID, or reads callable inputs at `/#steps.1.inputs.<name>` instead of `/#steps.1.<name>`.
- [ ] **Saved, or only edited?** Canvas changes don't apply until you **Save**, and **Edit workflow JSON** shows the last saved version. Changes then take about a minute to reach new runs.
- [ ] **Replaced?** Another enabled workflow with [`replaces`](https://developer.kustomer.com/docs/workflows/workflow-json#override-an-app-workflow-with-replaces) set to this workflow's ID stops it running.
- [ ] **Rate limited?** Events over your org's workflow limit are skipped, not queued. Look for bursts, such as bulk imports.
- [ ] **Did it run and then get undone?** Another workflow or an integration may be overwriting the change. Check the record's timeline.

## Test safely

- **Gate on a test marker.** While building, make the trigger's first condition require a tag such as `wf-test` (`contains [/#steps.1.tags, "<tagId>"]`). Only your test conversations run the workflow. Remove the gate when you go live.
- **Use a sandbox org** for anything that writes data, sends requests to partners or could loop.
- **Watch for loops.** A run count that climbs without new activity means the workflow is triggering itself. Disable it, then add a `changes` guard as described in [Workflows](https://developer.kustomer.com/docs/workflows#the-life-of-a-run).
- **Version your JSON.** Keep workflow JSON in source control so you can diff a revision that broke something against the last good one. Save it from [`GET /v1/workflows/{id}`](https://developer.kustomer.com/api/workflows/get-a-workflow-based-on-the-id) rather than **Export JSON**, which leaves out `response`, `enabled` and `logging`. `meta.workflowRev` on each failure tells you which revision ran, and the builder's version picker opens the 30 most recent versions read-only.
- **Add error cases in JSON.** The classic builder has no error-case editor. Use **Edit workflow JSON** or the API, as described in [Handle errors](https://developer.kustomer.com/docs/workflows/conditions-and-expressions#handle-errors).
