Skip to main content
This page is organized by symptom. Most issues are trace-shape problems: the data arrives, but it does not match the trace contract. For the ideal happy path and bad → better → best examples, see Building high-quality traces.
When behavior is unclear, first enable Debug mode in the same runtime that serves traffic. It shows whether the SDK is running, which trace it tries to send, how many child spans were included, and whether the ingest request succeeds.

Audit traces that already land

Use the Lemma diagnostics skill when traces are arriving and you want to check whether their shape matches the high-quality traces guide. The skill audits your traces without editing your app.
Then prompt your coding agent:
If no traces are arriving, start with Quickstart instead.

No traces in Lemma

Cause: credentials, project ID, base URL, or outbound network access is wrong. Fix: create one Lemma client with your API key and project ID, then send a tiny test trace.
  • Confirm LEMMA_API_KEY and LEMMA_PROJECT_ID come from the same project.
  • Confirm your runtime can reach https://api.uselemma.ai.
  • Pass baseUrl / base_url only when using a self-hosted or staging endpoint.
Debug with Debug mode: enable LEMMA_DEBUG=1 and run the smoke test again. If you do not see SDK logs, the traced code path is not running in that process. If you see an ingest failure, check the status code, credentials, project ID, and baseUrl.

Every span appears as a separate trace

Cause: child work is being recorded outside the lemma.trace() callback, so the SDK cannot attach it to the root trace. Fix: make lemma.trace() the boundary around the whole agent run, and record tools, generations, and spans inside that callback.
Distinguish two boundaries:
  • Successive conversation turns in different processes (request handler answers turn 1, a worker answers turn 2): start a new root for each turn and pass the same threadId / userId. See Instrumenting multi-turn agents.
  • One user turn on a host and a sandbox: export a context token, record a journal in the child with no API key, then apply and ingest() once. See Cross-process turns.
Debug with Debug mode: compare logs from the request handler and any background worker. If the child work runs after the lemma.trace() callback has returned, it will not attach to the root trace automatically. Move the recording into the callback, pass trace/span IDs to detached record* helpers from the coordinator, or use startTurn / attachTurn when the child is a separate process of the same turn.

Host and sandbox show up as two traces

Cause: each process started its own lemma.trace() and shared threadId / thread_id. Lemma groups those as two conversation turns, not one execution. Fix: export a context token on the host. Call attachTurn / attach_turn in the child with no API key. Apply the journal on the host and ingest() once. Keep LEMMA_API_KEY off the sandbox.
See Cross-process turns. Set threadId on the host root so this turn groups with the rest of the conversation. Debug with Debug mode: the host should log one sending trace / trace sent with sandbox children included. The child process should not log ingest at all.

Tool calls are missing

Cause: provider instrumentation captures model calls but not your application tools, or the tool result is never recorded. Fix: call trace.recordTool() after each tool completes.
For failures, record the error before re-raising:
Debug with Debug mode: check the spanCount (span_count in Python) on the sending trace log. If the count does not increase after you add recordTool(...), the tool recording code path is not running or is running outside the trace.

Inputs or outputs are blank

Cause: the trace was created without input, and the callback did not return or set the final output. Fix: pass input to lemma.trace() and return the final answer. Use trace.output() only when the callback return value is not the user-visible output.
Debug with Debug mode: confirm the trace logs trace sent, then open that trace in Lemma. If root output is still blank, the callback likely returned undefined, streamed the answer elsewhere, or exited before setting trace.output(...).

Root input/output is null after a second delivery

Cause: the trace was delivered again with a sparse ingest() payload, for example a follow-up call that carries a late child span but omits root input/output. ingest() is not an incremental merge API, so omitted root fields do not preserve prior values, and analysis already ran once after the first complete delivery. Fix: send one complete trace when the execution finishes: root input/output, thread/user, and all child spans in a single ingest() call. Retries of that same complete payload are safe (already-stored span IDs are skipped). Across queues and workers, assemble the full tree in a coordinator and ingest once rather than streaming partial deliveries under one trace.id. For a host and sandbox that are one turn, apply the child journal on the host and ingest once. See Cross-process turns. Debug with Debug mode: check the sending trace log for each delivery. If a later send has a small spanCount (span_count in Python) and no root I/O, that sparse payload is overwriting the root fields. Consolidate it into the single terminal send.

Model or content is missing

Cause: the LLM call is recorded as a generic span, so Lemma has no model, prompt, or completion to read as a generation. Fix: record model calls with trace.recordGeneration().
See Generations. Debug with Debug mode: check whether spanCount (span_count in Python) increases when the model call runs. If it does but the dashboard still lacks model/content metadata, switch that call site from generic span recording to recordGeneration() / record_generation() and include model, input, and output.

Agent name is blank

Cause: no stable name is attached to the trace, so traces cannot be grouped or filtered by workflow. Fix: set name on every lemma.trace() call.
Debug with Debug mode: check the name on trace started or sending trace. It should be the stable workflow name you expect, not a generated fallback.

Trace nesting is flat

Cause: long-running subtasks are recorded as sibling observations when they should have their own parent span. Fix: use trace.startSpan() / trace.start_span() when a subtask has nested work. In TypeScript, record children from the span handle. In Python, pass the parent span handle’s id as parent_id when starting child work.
Debug with Debug mode: confirm spanCount (span_count in Python) includes both the parent span and the child tool, then inspect the trace in Lemma. If they appear as siblings, record the child from the parent span handle in TypeScript, or pass parent_id=retrieve.id when starting the child in Python, before ending the parent span.

Serverless traces are delayed or missing

Cause: the request exits before the SDK request finishes, or a background task records work after the trace callback has returned. Fix: await lemma.trace(...) inside the handler and keep trace recording inside the callback. The SDK sends the completed payload before lemma.trace() resolves. Debug with Debug mode: check whether the ingest log appears before the function returns. If it appears after the response is sent, or never appears, await the trace promise and avoid detached background recording unless you explicitly end the trace handle.

Traces render but there are no issues

Cause: the trace renders, but it is not in a shape Lemma recognizes for automated issue detection. Fix: match the contract: one root trace, input/output, typed generation children, typed tool children, error states, and thread/user context where available.
  • Use lemma.trace() for the root.
  • Use trace.recordGeneration() / trace.record_generation() for model calls.
  • Use trace.recordTool() / trace.record_tool() for tool calls.
  • Use trace.recordSpan() / trace.record_span() for completed app work or trace.startSpan() / trace.start_span() for live app work.
  • Pass threadId / thread_id and userId / user_id for multi-turn analysis.
  • If one turn spans a host and a sandbox, use startTurn / attachTurn / apply. See Cross-process turns.
Debug with Debug mode: confirm the SDK logs trace sent with the expected trace name and child spanCount (span_count in Python), then compare the dashboard trace against the contract.