You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -4,47 +4,371 @@ description: This article explains why the input and output of a trace might be
4
4
tags: [observability, observability-get-started]
5
5
---
6
6
7
-
# Why are the input and output of a trace empty?
7
+
# Why are the input and output of my trace empty?
8
8
9
-
By default, the input and output of a trace are set from the root observation (the first span or generation in your trace). If these fields are empty, it might be because the root observation did not have its input/output set, or they were not explicitly set at the trace level. You can explicitly set trace input/output using the update_trace method:
9
+
Having input and output on your traces affects several important features:
10
10
11
+
-**Browsing traces**: The traces list shows input/output previews, making it easier to find what you're looking for
12
+
-**Evaluations**: LLM-as-a-judge evaluators use trace input/output to assess quality. Empty fields mean evaluations won't work
13
+
-**Search**: You can search traces by their input/output content, but only if it's populated
14
+
15
+
## How trace input/output works
16
+
17
+
Before jumping into solutions, this section helps to understand how Langfuse populates trace input/output.
18
+
19
+
### Traces and observations
20
+
21
+
In Langfuse, a **trace** represents a complete request or operation. Inside each trace, you have **observations**.
22
+
23
+
```
24
+
Trace: "User asks about weather"
25
+
├── Observation: Parse user intent
26
+
├── Observation: Call weather API
27
+
└── Observation: Generate response (LLM call)
28
+
```
29
+
30
+
Both traces and observations can have their own input/output fields. They serve different purposes:
|**What it represents**| The overall request and response | Each individual step |
35
+
|**Where you see it**| Traces list, evaluations | Inside the trace detail view |
36
+
|**How it's set**| Inherited from root observation OR set explicitly | Set on each observation |
37
+
38
+
### The "root observation" rule
39
+
40
+
By default, trace input/output is copied from the observation at the top level (called the "root observation").
41
+
42
+
This means:
43
+
- If your root observation has input/output → the trace will too
44
+
- If your root observation has no input/output → your trace will be empty (unless you set it explicitly, see [below](#solution-b))
45
+
46
+
This is why your trace might show empty fields even though you can see data in the individual observations inside it.
47
+
48
+
## Troubleshooting
49
+
50
+
The most common reasons for empty trace input/output are:
51
+
52
+
### 1. You're using a short-lived application (scripts, serverless, notebooks)
53
+
54
+
**Symptoms**: Traces sometimes appear with missing data, or don't appear at all.
55
+
56
+
[Langfuse sends data in the background to keep your application fast](/docs/observability/data-model#background-processing). If your script or serverless function exits before the data is sent, it gets lost.
57
+
58
+
**Solution**: Call `flush()` before your application exits.
59
+
60
+
<Tabsitems={["Python", "JS/TS"]}>
61
+
<Tab>
11
62
```python
12
63
from langfuse import get_client
13
64
14
65
langfuse = get_client()
15
66
16
-
with langfuse.start_as_current_observation(as_type="span", name="complex-pipeline") as root_span:
### 3. You're using the `@observe()` decorator but input/output capture is disabled
210
+
211
+
**Symptoms**: Decorated functions don't show input/output, even though they return values.
212
+
213
+
The `@observe()` decorator [automatically captures function arguments as input and return values as output](/docs/observability/sdk/instrumentation#observe-wrapper). But this can be disabled.
### 4. You're using an OpenTelemetry-based integration
244
+
245
+
**Symptoms**: Traces from OTEL integrations (OpenLLMetry, Logfire, etc.) show empty input/output.
246
+
247
+
Different OpenTelemetry providers use different attribute names for input/output. Langfuse looks for specific attributes and may not find them if your provider uses different names.
248
+
249
+
**Solution**: Set the attributes Langfuse expects
250
+
251
+
Langfuse maps these OTEL span attributes to observation input/output (checked in this order):
252
+
253
+
| Observation field | OTEL attributes (in priority order) |
<summary>**Which attributes does my OTEL provider use?**</summary>
307
+
308
+
Different providers use different semantic conventions. Here's how to find out what your provider sends:
309
+
310
+
1.**Enable debug logging** in your OTEL exporter to see the raw span attributes
311
+
2.**Check the trace in Langfuse**: Open a trace, click on an observation, and look at the "Metadata" tab to see which attributes were received
312
+
3.**Consult your provider's documentation** for their semantic conventions
313
+
314
+
Common providers and their conventions:
315
+
-**OpenLLMetry**: Uses `gen_ai.prompt` and `gen_ai.completion`
316
+
-**OpenInference**: Uses `input.value` and `output.value`
317
+
-**MLflow**: Uses `mlflow.spanInputs` and `mlflow.spanOutputs`
318
+
-**Pydantic Logfire**: Uses custom attributes (Langfuse has specific support since PR #5841)
319
+
320
+
If your provider uses different attribute names, you have two options:
321
+
1. Manually set the attributes Langfuse expects (as shown above)
322
+
2. Open a [GitHub issue](https://github.com/langfuse/langfuse/issues) requesting support for your provider's conventions
323
+
324
+
</details>
325
+
326
+
Many OTEL-specific issues have been fixed in recent Langfuse versions. If you're self-hosting, make sure you're on the latest version.
327
+
328
+
---
329
+
330
+
## Still having issues?
331
+
332
+
If none of the above solutions work:
333
+
334
+
1.**Enable debug logging** to see what's being sent:
335
+
336
+
<Tabsitems={["Python", "JS/TS"]}>
337
+
<Tab>
338
+
```python
339
+
from langfuse import Langfuse
340
+
341
+
langfuse = Langfuse(debug=True)
342
+
```
343
+
</Tab>
344
+
<Tab>
345
+
```typescript
346
+
const langfuse =newLangfuse({ debug: true });
347
+
```
348
+
</Tab>
349
+
</Tabs>
350
+
351
+
2.**Check your SDK version** and update if needed:
352
+
353
+
<Tabsitems={["Python", "JS/TS"]}>
354
+
<Tab>
355
+
```bash
356
+
pip install --upgrade langfuse
357
+
```
358
+
</Tab>
359
+
<Tab>
360
+
```bash
361
+
npm update langfuse
362
+
```
363
+
</Tab>
364
+
</Tabs>
365
+
366
+
3.**Look at the trace structure** in the Langfuse dashboard:
367
+
- Open a trace and look at the observation tree
368
+
- Is there a single root observation?
369
+
- Do the individual observations have input/output?
49
370
50
-
If you use the decorator-based integration, input/output are captured automatically, but you can override or disable this behavior using parameters like capture_input, capture_output, or by calling update_current_trace as shown above[(2)](/docs/observability/sdk/instrumentation#trace-inputoutput-behavior)[(1)](https://langfuse.com/docs/observability/sdk/overview).
371
+
4.**Ask for help**: Open a [GitHub discussion](https://github.com/langfuse/langfuse/discussions) with:
372
+
- Your SDK version
373
+
- A code snippet showing how you're creating traces
374
+
- A screenshot of the trace structure in the dashboard
0 commit comments