Skip to main content

Documentation Index

Fetch the complete documentation index at: https://braintrust.dev/docs/llms.txt

Use this file to discover all available pages before exploring further.

Applies to:
  • Plan -
  • Deployment -

Summary

Issue: Calling traced() with a parent value from span.export() throws SpanComponents string is not properly encoded. This library only supports encoding versions up to 3, or feedback spans fail to attach to nested spans created by wrapTraced(). Cause: The encoding version mismatch occurs when one endpoint uses @braintrust/otel or BRAINTRUST_OTEL_COMPAT (producing V4 SpanComponents) while the other uses the default V3 decoder; transport corruption or incorrect span targeting can also cause failures. Resolution: Ensure consistent OTel compatibility settings across endpoints, preserve exported span strings exactly during transport, and call span.export() from the correct span’s callback.

Resolution steps

If you see the SpanComponents encoding version error

Step 1: Check OTel compatibility settings

Verify whether either the generation endpoint or feedback endpoint imports @braintrust/otel or sets BRAINTRUST_OTEL_COMPAT=true. Both endpoints must use identical settings.
// If using OTel compatibility, both endpoints need this
import "@braintrust/otel";

Step 2: Align OTel initialization

If using OTel compatibility mode, ensure both endpoints initialize it before any Braintrust tracing code runs and use @braintrust/otel version 0.4.5 or later. If not using OTel, remove any @braintrust/otel imports or BRAINTRUST_OTEL_COMPAT flags from all endpoints.

If the exported span string passes through URLs

Step 1: Encode for URL transport

The string from span.export() contains characters like +, /, and = that must be preserved exactly. Use encodeURIComponent when passing via URLs.
// Sending
const encoded = encodeURIComponent(await span.export());

// Receiving  
const spanId = decodeURIComponent(encodedParam);
JSON transport requires no additional encoding.

If feedback isn’t attaching to nested wrapTraced spans

Step 1: Understand currentSpan() behavior

currentSpan() inside a wrapTraced callback returns the span created by that specific wrapper, not any inner spans created by the wrapped function.
const wrappedGenerateFunction = wrapTraced(
  async () => {
    const results = await generateFunction(); // may create its own spans
    const spanId = await currentSpan().export(); // exports the wrapper's span, not generateFunction's spans
    return { result: results, spanId };
  },
  { name: "generation" }
);

Step 2: Export from the target span

To attach feedback to a span created inside generateFunction(), call export() from within that function’s own wrapTraced callback, not from the outer wrapper.

Step 3: Use root span as alternative

If targeting nested spans proves difficult, export and use the root trace span instead:
// This works for any feedback that applies to the overall trace
const rootSpanId = await currentSpan().export(); // from outermost wrapper

For multiple feedback submissions

Create one child feedback span per submission rather than overwriting the same span:
// Call once per feedback event with the same parent spanId
await traced(
  async (span) => {
    span.log({ 
      scores: { user_rating: score },
      comment: comment,
      metadata: { timestamp: new Date().toISOString() }
    });
  },
  { name: "user_feedback", parent: generationSpanId }
);