JS/TS v4 → v5
The JS/TS SDK v5 introduces the observation-centric data model. In this model, correlating attributes (userId, sessionId, metadata, tags) propagate to every observation rather than living only on the trace. This enables single-table queries without expensive joins, significantly improving query performance at scale.
This changes how you set trace attributes: instead of imperatively updating the trace with updateActiveTrace(), you use propagateAttributes() — a function that wraps a callback, automatically applying attributes to all child observations created within its scope.
Breaking Changes
1. updateActiveTrace() decomposed into 3 functions
In the new model, correlating attributes (userId, sessionId, metadata, tags) must live on every observation, not just the trace. propagateAttributes() wraps a callback — the current and all child spans created inside the callback automatically inherit the attributes. Spans created before the callback are not retroactively updated.
v4:
import { updateActiveTrace, startActiveObservation } from "@langfuse/tracing";
await startActiveObservation("my-operation", async (span) => {
updateActiveTrace({
name: "user-workflow",
userId: "user-123",
sessionId: "session-456",
tags: ["production"],
public: true,
metadata: { testRun: "server-export" },
input: { query: "hello" },
output: { response: "world" },
});
});v5:
import {
propagateAttributes,
startActiveObservation,
setActiveTraceIO,
setActiveTraceAsPublic,
} from "@langfuse/tracing";
await propagateAttributes(
{
traceName: "user-workflow", // was "name"
userId: "user-123",
sessionId: "session-456",
tags: ["production"],
metadata: { testRun: "server-export" },
},
async () => {
await startActiveObservation("my-operation", async (span) => {
setActiveTraceIO({
input: { query: "hello" },
output: { response: "world" },
});
setActiveTraceAsPublic();
});
},
);Key differences:
| Attribute | v4 | v5 |
|---|---|---|
name | updateActiveTrace({name: ...}) | propagateAttributes({traceName: ...}, cb) |
userId, sessionId, tags, version | updateActiveTrace({...}) | propagateAttributes({...}, cb) |
metadata | updateActiveTrace({metadata: any}) | propagateAttributes({metadata: Record<string,string>}, cb) |
input, output | updateActiveTrace({...}) | setActiveTraceIO({...}) (deprecated) |
public | updateActiveTrace({public: true}) | setActiveTraceAsPublic() |
release | updateActiveTrace({release: ...}) | Removed — use LANGFUSE_RELEASE env var |
environment | updateActiveTrace({environment: ...}) | Removed — use LANGFUSE_TRACING_ENVIRONMENT env var |
setActiveTraceIO() is deprecated and exists only for backward compatibility
with trace-level
LLM-as-a-judge
evaluators that rely on trace input/output. For new code, set input/output on
the root observation directly.
2. .updateTrace() → .setTraceIO() + .setTraceAsPublic()
The same decomposition applies on all observation wrapper classes (LangfuseSpan, LangfuseGeneration, etc.).
v4:
import { startObservation } from "@langfuse/tracing";
const span = startObservation("my-op");
span.updateTrace({
name: "my-trace",
userId: "user-123",
sessionId: "session-456",
tags: ["prod"],
public: true,
input: { query: "hello" },
output: { response: "world" },
});v5:
import { propagateAttributes, startObservation } from "@langfuse/tracing";
propagateAttributes(
{
traceName: "my-trace",
userId: "user-123",
sessionId: "session-456",
tags: ["prod"],
},
() => {
const span = startObservation("my-op");
span.setTraceIO({
input: { query: "hello" },
output: { response: "world" },
});
span.setTraceAsPublic();
span.end();
},
);.setTraceIO() is deprecated and exists only for backward compatibility with
trace-level
LLM-as-a-judge
evaluators that rely on trace input/output.
3. @langfuse/langchain internal changes
The CallbackHandler now uses propagateAttributes() for trace-level attributes. This affects users who:
- Subclass
CallbackHandler - Depend on the internal span-creation behavior
- Rely on
traceMetadataaccepting non-string values — non-string values are now serialized viaJSON.stringifybefore being passed topropagateAttributes, which requiresRecord<string, string>
4. @langfuse/openai internal changes
The traceMethod wrapper now wraps the traced call in propagateAttributes() to set userId, sessionId, tags, and traceName, instead of calling .updateTrace() on the observation. (If you rely on attributes being set on parent observations as well, wrap the entire execution in with propagateAttributes).
5. Removed attributes
| Removed | Replacement |
|---|---|
release | Set via LANGFUSE_RELEASE env var |
environment | Set via LANGFUSE_TRACING_ENVIRONMENT env var |
public | Replaced by setActiveTraceAsPublic() / .setTraceAsPublic() |
Migration Checklist
- Search for
updateActiveTrace→ split intopropagateAttributes()+setActiveTraceIO()(when relying on legacy trace-level LLM-as-a-judge configurations) +setActiveTraceAsPublic() - Search for
.updateTrace(→ split intopropagateAttributes()+.setTraceIO()+.setTraceAsPublic() - Verify propagated metadata values are
Record<string, string>with values ≤200 characters - Replace
release/environmentattribute usage with env vars (LANGFUSE_RELEASE,LANGFUSE_TRACING_ENVIRONMENT)