Skip to main content
After this guide, you can go from “the agent answered wrongly” to the exact log entry that explains it, and fix the code once instead of pushing blind. The loop is the same in the sandbox and in production: reproduce with one message, read the entries that run wrote, change the code, repeat. For the pre-release rungs (lua test, workflow drivers, voice tests) see Test an agent before you release. Verified against lua-cli 3.33.0. Before you begin
  • A project signed in with lua auth configure; lua logs reads the agent in lua.skill.yaml (--agent-id <id> overrides it for another agent you administer).
  • Something to inspect: a conversation, a job run, a webhook call, or a workflow run that already happened.
1

Reproduce on an isolated thread

Send the smallest message that triggers the code path, on a fresh thread so earlier turns cannot influence the answer. The sandbox runs sandbox versions compiled from your local code; production runs the promoted version.
When the bug is in one function rather than in the model’s decision, skip the conversation and call the function with lua test skill --name <tool-name> --input '<json>'; its output is exactly what the tool returned, with no log lookup needed.
2

Read the entries that run wrote

Filter by primitive type and, for skills, jobs, webhooks, processors, and devices, by name. Entries print oldest first.
Output
Each tool call writes Calling tool with input …, then Execute function completed in <n> ms and Tool result …, or an ERROR entry with the thrown message. Anything your code prints with console.log appears as a DEBUG entry and console.error as an ERROR entry, so log the raw return value of a platform call before you transform it and read its real shape here. A message field is stored up to 256 KB and then cut with [truncated; original length <n>].
3

Widen or narrow the filter

--type accepts all, skill, job, webhook, preprocessor, postprocessor, device, device-trigger, user_message, agent_response, agent_error, mcp, rag, runtime, and calls. --name works with skill, job, webhook, preprocessor, postprocessor, and device. agent_error holds failures on the message path such as billing, validation, and model errors; runtime holds the agent runtime’s own lines; user_message and agent_response show what was said, with the channel and end user ID.
--limit defaults to 20 and the server caps it at 100; use --page for older entries. There is no time filter and no environment filter: sandbox and production entries are stored together, and metadata.channel is dev for every message sent from lua chat in either environment, so bound a check by timestamp and, for one conversation, by --user-id. The flag table is on the lua logs reference.
4

Script it with --json

--json returns { logs, pagination }. Each entry carries subType (error, warn, info, debug, start, or complete; there is no level field), message, an optional duration in milliseconds, and metadata with logSource, primitiveName, primitiveId, toolName, userId, and channel.
Output
Select errors with jq '.logs[] | select(.subType == "error")'.
5

Debug the CLI itself

When a command fails, it prints one line, ✖ <code>: <message>, plus a 💡 hint. Set LUA_DEBUG=1 (or pass --debug to lua compile) to print the stack trace under that line.
The exit code tells you the class of failure before you read the message; see Errors and exit codes.
6

Verify a deploy

After lua version promote or lua deploy, note the time, send one production message on a fresh thread, and confirm that nothing was written since under agent_error or as an error under skill. A throwing tool lands under skill, not agent_error, and the logs have no time filter, so the check reads both types and bounds them by timestamp.
jq -e exits 1 when the filtered array is not empty, which is what a CI job needs; see Automate releases in CI.

Options you may need

Read trigger, job, workflow, and call records

Some primitives keep their own execution records in addition to lua logs:
lua triggers logs shows up to 200 executions; lua jobs history shows the last 20 with status, duration, result, and error; lua workflows status --steps prints per-step state and output previews, lua workflows logs the run’s event stream (--follow keeps it open), and job-logs the container output of a Job-tier step. See the lua triggers, lua jobs, and lua workflows references.

Keep secrets out of the logs

The runtime’s own entries (runtime, workflow events, device command audits) redact bearer tokens, JWTs, and token=, api_key=, secret=, and password= values, and device command payload fields such as content and password are replaced with byte counts. Your own console.log output is stored as printed: do not log env('…') values or request headers. Local lua test runs write no server entries at all.

If it isn’t working

--name filters an entity list, so the CLI needs to know which list. Pass --type skill|job|webhook|preprocessor|postprocessor|device with it; message types (user_message, agent_response, agent_error, runtime, mcp, rag) have no names.
The hint lists the accepted values. --type all shows everything; runtime is the type for agent runtime and model-provider lines.
Exit 3. --name takes the primitive’s name as it appears on the server (lua skills view, lua jobs view, lua webhooks view), or its ID. For a skill it is the skill name, not a tool name.

Next steps

lua logs reference

Every filter and the entry schema.

Test an agent before you release

Reproduce with exact input before reading logs.

Troubleshoot the CLI

Exact error strings by stage, with fixes.

About security and data

Where logs live and what is redacted.