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. --since, --until, --environment, and --follow ship with log drains in lua-cli 3.38.0; on an earlier CLI they are unknown options. 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. When you want to filter on a value rather than read it, log.info(message, fields) writes the same entry with the fields beside the message, where --json shows them under metadata.fields. 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, calls, and all eighteen log sources: skill, job, webhook, trigger, preprocessor, postprocessor, device, device-trigger, user_message, agent_response, agent_error, mcp, rag, runtime, model-resolver, workflow, workflow-step, and workflow-script. --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; model-resolver says which model a turn resolved to; 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. --since and --until bound the window β€” an ISO 8601 instant, or a relative 15m, 2h, 7d β€” and --environment production|sandbox picks one environment, since entries from both are stored together. --follow keeps the command open and prints new entries as they are written. The flag table is on the lua logs reference.
Watch one primitive live while you reproduce a bug in another terminal: lua logs --type skill --name tickets --follow.
4

Script it with --json

--json returns { logs, nextCursor, 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, channel, environment, agentVersion, and executionId. Page with nextCursor rather than --page: above 10,000 matches pagination.totalCount is capped at 10000 and the boolean pagination.totalCountExact is false. hasNextPage is reliable either way.
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 a release, 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, so the check reads both types; --since and --environment bound it to the window and the environment you just changed.
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.

Ship the same records to your own stack

lua logs is the platform’s own copy. A log drain copies the same records to a destination you own β€” your own HTTPS receiver, an OpenTelemetry collector, Datadog, or Better Stack β€” as they happen, so alerting and long-term search live where the rest of your telemetry does. It is a copy, not a move: everything a drain sends is still readable here.
A drain is configured on the organization and selects agents, sources, severities, and environments. Records leave from Lua’s own network rather than from your agent’s sandbox, so a receiver behind an IP allow-list needs the egress ranges rather than a DNS name; those ranges are not published yet, and that section says where they will be. Deliveries to your own HTTPS endpoint are signed β€” see Verify signatures.

Scopes for reading logs

lua logs works with any credential that can administer the agent. Over REST, reading logs needs logs:read and configuring drains needs logs:manage, which is a sensitive scope that a wildcard grant does not satisfy. The scope each log route used before is still accepted for two releases β€” knowledge:read on an agent’s logs, automations:read on a skill’s logs, analytics:read on the organization’s logs and their CSV export β€” and a request authorized by one of those answers with Deprecation: true and a Sunset date. See Log drains REST.

How long entries are kept

Execution logs are kept for 90 days by default, then deleted. Anything you need beyond that window belongs in a drain β€” or in a scheduled export of your own built on lua logs --since/--until --json. See Security and data.

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, what is redacted, and how long entries are kept.

Ship logs to your stack

Copy these records to Datadog, OTLP, or your own receiver.

lua drains

Create, verify, and watch a drain.