> ## Documentation Index
> Fetch the complete documentation index at: https://tracevell.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Continuing Traces

When a request spans multiple services, you want one connected trace—not isolated fragments. This requires passing span context between services.

## Context Propagation

Span context is a serialized representation of the current span. Pass it to downstream services, and they can continue the same trace.

Common patterns:

* **HTTP headers** — Include serialized context in a custom header (e.g., `X-Tracevell-Span-Context`)
* **Message queues** — Embed context in the message payload alongside your data
* **Database storage** — Store context with workflow state for long-running processes that span multiple requests

The downstream service deserializes the context and uses it as the parent for its spans. The result: a single trace showing the full request flow across services.

## Example

When you can’t pass span objects directly (HTTP, queues, cron jobs), serialize context in the upstream service and deserialize in the downstream service.

> [tab] TypeScript
>
> ```typescript theme={null}
> import { Tracevell, observe } from '@lmnr-ai/lmnr';
>
> // Service A
> await observe({ name: 'serviceAHandler' }, async () => {
>   const context = Laminar.serializeLaminarSpanContext();
>   await fetch('https://service-b/api', {
>     headers: { 'X-Tracevell-Span-Context': context ?? '' },
>   });
> });
>
> // Service B
> const parentSpanContext = req.headers['x-tracevell-span-context'];
> const span = Laminar.startSpan({
>   name: 'serviceBHandler',
>   parentSpanContext: parentSpanContext as string | undefined,
> });
> span.end();
> ```
>
> See also: [`Laminar.serializeLaminarSpanContext`](https://tracevell.com/docs/sdk/context-utilities#ts-tracevell-serialize-span-context) and [`Laminar.startSpan`](https://tracevell.com/docs/sdk/manual-spans#ts-tracevell-start-span)

> [tab] Python
>
> ```python theme={null}
> from lmnr import Tracevell, observe
>
> # Service A
> @observe()
> def service_a_handler():
>     context = Laminar.serialize_span_context()
>     requests.post(
>         "https://service-b/api",
>         headers={"X-Tracevell-Span-Context": context or ""},
>     )
>
> # Service B
> parent = Laminar.deserialize_span_context(request.headers.get("X-Tracevell-Span-Context"))
> with Laminar.start_as_current_span(
>     name="service_b_handler",
>     parent_span_context=parent,
> ):
>     handle_request()
> ```
>
> See also: [`Laminar.serialize_span_context`](https://tracevell.com/docs/sdk/context-utilities#py-tracevell-serialize-span-context) and [`Laminar.deserialize_span_context`](https://tracevell.com/docs/sdk/context-utilities#py-tracevell-deserialize-span-context)

## Common Patterns

* **Database storage:** store serialized context alongside workflow state, then reuse it when the workflow resumes.
* **Message queues:** include context in the message payload so the consumer can continue the trace.

### Database Storage Pattern

Persist span context with workflow state so you can resume a long-running trace later.

> [tab] TypeScript
>
> ```typescript theme={null}
> import { Tracevell, observe } from '@lmnr-ai/lmnr';
>
> // Start workflow and persist context
> await observe({ name: 'workflow_start' }, async () => {
>   const spanContext = Laminar.serializeLaminarSpanContext();
>   await db.saveWorkflow({ userId, spanContext, data: workflowData, status: 'started' });
> });
>
> // Later: resume workflow and continue trace
> const workflow = await db.getWorkflow(workflowId);
> const span = Laminar.startSpan({
>   name: 'workflow_continue',
>   parentSpanContext: workflow.spanContext ?? undefined,
> });
> try {
>   // Continue processing...
> } finally {
>   span.end();
> }
> ```

> [tab] Python
>
> ```python theme={null}
> from lmnr import Tracevell
>
> def start_workflow(user_id: str, workflow_data: dict):
>     with Laminar.start_as_current_span(name="workflow_start") as span:
>         span_context = Laminar.serialize_span_context(span)
>         db.save_workflow(
>             {
>                 "user_id": user_id,
>                 "span_context": span_context,
>                 "data": workflow_data,
>                 "status": "started",
>             }
>         )
>
> def continue_workflow(workflow_id: str):
>     workflow = db.get_workflow(workflow_id)
>     parent = Laminar.deserialize_span_context(workflow.get("span_context"))
>     with Laminar.start_as_current_span(
>         name="workflow_continue",
>         parent_span_context=parent,
>     ):
>         # Continue processing...
>         pass
> ```

### Message Queue Pattern

Include span context in your queued message payload so consumers can continue the trace.

> [tab] TypeScript
>
> ```typescript theme={null}
> import { Tracevell, observe } from '@lmnr-ai/lmnr';
>
> // Producer
> await observe({ name: 'task_enqueued' }, async () => {
>   const spanContext = Laminar.serializeLaminarSpanContext();
>   await queue.send({ data: taskData, spanContext });
> });
>
> // Consumer
> const message = await queue.receive();
> const span = Laminar.startSpan({
>   name: 'task_processed',
>   parentSpanContext: message.spanContext ?? undefined,
> });
> try {
>   // Process the task...
> } finally {
>   span.end();
> }
> ```

> [tab] Python
>
> ```python theme={null}
> from lmnr import Tracevell
>
> def enqueue_task(task_data: dict):
>     with Laminar.start_as_current_span(name="task_enqueued"):
>         span_context = Laminar.serialize_span_context()
>         queue.send({"data": task_data, "trace_context": span_context})
>
> def process_task(message: dict):
>     parent = Laminar.deserialize_span_context(message.get("trace_context"))
>     with Laminar.start_as_current_span(
>         name="task_processed",
>         parent_span_context=parent,
>     ):
>         # Process the task...
>         pass
> ```

## Notes

* **Within a service:** prefer passing span objects and activating them (`withSpan` / `use_span`) instead of serializing context (see `sdk/manual-spans`).
* **When context is unavailable:** if deserialization fails or context is missing, start a new trace rather than crashing.
* **Treat context as untrusted input:** validate and fail open (see `sdk/context-utilities`).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.