One agent execution = one trace. If you fix only one thing, make every LLM and tool call a child span of a single root rather than its own top-level trace.
Sibling traces vs one nested trace
The most common problem: each LLM or tool call is exported as its own top-level trace, so a single agent run is scattered across three unrelated traces. Bad - three sibling traces, one per call:- Cause: child work ran without an explicit root trace or trace ID, or it crossed a queue, worker, stream, or timer without carrying the trace handle/IDs with it.
- Fix: wrap the run with
lemma.trace()and record child work withtrace.recordGeneration()/trace.record_generation(),trace.recordTool()/trace.record_tool(), andtrace.recordSpan()/trace.record_span()inside it. See Traces and Every span appears as a separate trace.
Empty root vs root with input and output
A trace whose root carries no input or output shows only timing. You cannot read what the user asked, what the agent answered, or whether the run succeeded. Bad - root with no input/output:- Cause: the root span was never given input/output (or the work was recorded only on children).
- Fix: pass
inputtolemma.trace()and return the final answer from the callback. Usetrace.output(output)for custom output andtrace.fail(error)before re-raising errors. See Traces.
Invisible tool calls vs typed tool spans
Provider instrumentation usually captures model calls but not your application tools, so the model is visible while the tools it called are not. Even when a tool span exists, an untyped one with no args or result is opaque. Bad - provider/LLM spans present, tool calls invisible:- Cause: tools are invoked outside any span, or are wrapped in a generic span that is not typed as a tool.
- Fix: record each completed tool call with
trace.recordTool({ name, input, output })ortrace.record_tool(name=..., input=..., output=...). The SDK records the arguments, result, and tool kind attributes Lemma reads. See Tool calls.
Flat nesting vs a proper parent/child tree
Spans can all exist yet sit as siblings under the root, losing the structure of which step called which. A flat list hides retries, sub-agents, and the retrieval that fed a generation. Bad - flat: every span is a direct child of the root:- Cause: child spans are created without the explicit parent span handle or parent ID, so everything attaches to the root.
- Fix: record child work from the
traceor span handle passed into the helper. Usetrace.startSpan()/trace.start_span()when a subtask needs its own measured parent span. See Trace nesting is flat.
Current issue-detection support
Beyond rendering a trace, Lemma runs automated issue detection (silent failures, bad tool calls, loops). Today this runs for traces that arrive in a recognized shape:- Lemma SDK traces produced by
lemma.trace()withtrace.recordGeneration()/trace.record_generation()andtrace.recordTool()/trace.record_tool()child records.
Trace contract
The exact shape and fields Lemma reads.
Common issues
Symptoms, causes, and fixes for malformed traces.
Traces
Wrap one agent execution in one trace.
Tool calls
Record tool args and results as child spans.