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

# Troubleshooting

> Diagnose and fix common issues when installing or using the Braintrust TypeScript SDK.

> [accordion] API URL conflicts in self-hosted deployments
>
> **Issue:** Streaming logs fail with initialization or forbidden errors when `apiUrl`, `appUrl`, or `BRAINTRUST_API_URL` point to conflicting environments.
>
> **Cause:** The SDK sends logging requests to the API/data plane URL. If `apiUrl` is set, it takes precedence over `appUrl`.
>
> **Fix:** For Braintrust-hosted projects, remove custom URL settings unless you use a custom frontend. For self-hosted or hybrid projects, set only `apiUrl` or `BRAINTRUST_API_URL` to the data plane endpoint.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import { initLogger } from "braintrust";
>
> initLogger({
>   projectName: process.env.BRAINTRUST_PROJECT_NAME,
>   apiKey: process.env.BRAINTRUST_API_KEY,
>   apiUrl: "https://your-data-plane.example.com",
> });
> ```

> [accordion] Evals fail from resource exhaustion or EMFILE errors
>
> **Issue:** Evals fail, time out, produce incomplete results, or hit `EMFILE: too many open files`.
>
> **Cause:** `Eval()` runs tasks with unlimited concurrency unless `maxConcurrency` is set. That can exhaust file descriptors, memory, database pools, or model-provider rate limits.
>
> **Fix:** Set `maxConcurrency` to a small value such as `10`, then tune up or down based on the workload.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import { Eval } from "braintrust";
>
> await Eval("my-project", {
>   data: myDataset,
>   task: myTask,
>   scores: [myScorer],
>   maxConcurrency: 10,
> });
> ```

> [accordion] Eval process hangs after completion
>
> **Issue:** `braintrust eval` prints results successfully, but the Node.js process does not exit.
>
> **Cause:** Open handles in your eval process can keep the Node.js event loop alive. Common sources include database pools, Redis clients, WebSockets, and HTTP keep-alive agents.
>
> **Fix:** Run the eval with the Node inspector to identify active handles, then explicitly close external resources after the eval finishes.
>
> ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> node --inspect-brk ./node_modules/.bin/braintrust eval your-eval.ts
> ```
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> process.on("beforeExit", async () => {
>   await mongoClient.close();
>   await redis.disconnect();
> });
> ```

> [accordion] Traces are missing, empty, or stuck in progress
>
> **Issue:** Traces don't appear, show empty fields, never leave "in progress", or don't include final values.
>
> **Cause:** A process can exit before logs flush, or an exception can bypass `end()` on a manually-created span.
>
> **Fix:** Prefer `traced()`, which ends the span for you, even when the wrapped function throws. If you use `startSpan()` directly, call `end()` in `finally`. Either way, call `flush()` before a short-lived process exits. In long-lived Node.js processes, v3.40.0 and later hold logged rows for up to 1 second before sending them, and the SDK's automatic flush at exit doesn't run when the process exits through `process.exit()` or a signal. Call `flush()` in those shutdown paths too. To turn off the delay, set [`BRAINTRUST_FLUSH_DELAY_MS`](https://docs.braintrust.dev/sdks/typescript/api-reference#environment-variables) to `0`.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import { flush, startSpan, traced } from "braintrust";
>
> await traced(async (span) => {
>   span.log({ input, output });
> });
>
> const span = startSpan({ name: "manual-operation" });
> try {
>   span.log({ input });
> } finally {
>   span.end();
>   await flush();
> }
> ```
>
> Check for logging failures after flushing. See [Trace data durability](https://docs.braintrust.dev/instrument.md#trace-data-durability) for the API's storage guarantee.

> [accordion] Trace continuity breaks after restarts
>
> **Issue:** Follow-up turns in an existing conversation stop appearing under the original trace after a server restart or pod replacement.
>
> **Cause:** Exported span context from `span.export()` was kept only in memory. After restart, the SDK cannot continue the previous trace.
>
> **Fix:** Persist the exported span context. On the next request, wrap the work in `withParent()`, or pass the stored value as `parent` to `traced()` or `startSpan()`.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import { traced, withParent } from "braintrust";
>
> const parent = await traced((span) => span.export(), { name: "conversation" });
> await db.set(conversationId, parent);
>
> const storedParent = await db.get(conversationId);
> if (storedParent) {
>   await withParent(storedParent, async () => {
>     await traced(async (span) => {
>       span.log({ input: nextMessage });
>     });
>   });
> }
> ```

> [accordion] Prompt version IDs do not match the UI
>
> **Issue:** `prompt.version` returns a large decimal string, while the Braintrust UI shows a short hexadecimal version ID.
>
> **Cause:** The SDK returns the raw transaction ID. The UI displays a prettified, reversible version of the same ID.
>
> **Fix:** Convert SDK values to the UI form with `prettifyXact()`, or back with `loadPrettyXact()`. Both are exported from `braintrust/util`.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import { loadPrettyXact, prettifyXact } from "braintrust/util";
>
> const uiVersion = prettifyXact(prompt.version);
> const rawVersion = loadPrettyXact(uiVersion);
> ```

> [accordion] Prompt shows None in experiment view
>
> **Issue:** The experiment view shows `Prompt: None`, even though your task uses a prompt.
>
> **Cause:** On LLM spans, the UI detects prompts from prompt metadata. Hard-coded prompts or prompts sent without the Braintrust prompt workflow do not attach that metadata.
>
> **Fix:** Fetch the prompt with `loadPrompt()`, call `prompt.build({ input })` to produce the messages and parameters, then send them through a wrapped client (`wrapOpenAI`, `wrapAnthropic`, etc.). The wrapper reads the metadata from `build()` and attaches it to the LLM span.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import { loadPrompt, wrapOpenAI } from "braintrust";
> import OpenAI from "openai";
>
> const client = wrapOpenAI(new OpenAI());
> const prompt = await loadPrompt({
>   projectName: "my-project",
>   slug: "summarizer",
> });
>
> const { messages, ...parameters } = prompt.build({ input });
>
> await client.responses.create({
>   ...parameters,
>   input: messages,
> });
> ```

> [accordion] GPT-5 prompts include unsupported temperature
>
> **Issue:** A prompt configured for a GPT-5 model returns `temperature` from `prompt.build()`, and passing those parameters to OpenAI fails.
>
> **Cause:** Whether a GPT-5 model accepts `temperature` depends on the reasoning effort. GPT-5.1 and later accept it only when `reasoning_effort` is `none`. At higher reasoning efforts, and for older GPT-5 models (`gpt-5`, `gpt-5-mini`, `gpt-5-nano`) and GPT-5 Pro, OpenAI rejects `temperature`. Re-saving the prompt in the Braintrust editor sanitizes its stored parameters for the selected model, but a prompt saved before this behavior can still return an unsupported `temperature`.
>
> **Fix:** Re-save the prompt in the Braintrust editor, or strip `temperature` from the parameters before sending the request, as shown below.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> const { temperature: _temperature, messages, ...parameters } = prompt.build(variables);
>
> await client.responses.create({
>   ...parameters,
>   input: messages,
> });
> ```

> [accordion] No traces sent from a deployment on Vercel
>
> **Issue:** You see only some traces, or none at all, from a deployment on Vercel.
>
> **Cause:** Vercel freezes the function as soon as it returns a response, so events still in Braintrust's buffer may never flush.
>
> **Fix:** Call `flush()` in your application code after your AI calls. On Vercel, `flush()` sends buffered data even after the response has been returned.
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> await client.responses.create();
>
> // Flushes buffered data, even on Vercel. flush() returns a Promise but does not need to be awaited.
> flush();
> ```

> [accordion] Auto-instrumentation crashes with a diagnostics\_channel kMaxChannels assertion
>
> **Issue:** Enabling [auto-instrumentation](https://docs.braintrust.dev/instrument/trace-llm-calls.md) crashes the Node.js process with a fatal `diagnostics_channel` assertion.
>
> ```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> # /usr/local/bin/node[1]: uint32_t node::diagnostics_channel::BindingData::GetOrCreateChannelIndex(const std::string&) at ../src/node_diagnostics_channel.cc:53
> # Assertion failed: (next_channel_index_) < (kMaxChannels)
> ----- Native stack trace -----
> ----- JavaScript stack trace -----
> 1: Channel (node:diagnostics_channel:196:32)
> 2: channel (node:diagnostics_channel:247:10)
> 3: tracingChannelFrom (node:diagnostics_channel:281:12)
> 4: TracingChannel (node:diagnostics_channel:301:16)
> 5: tracingChannel (node:diagnostics_channel:446:10)
> 6: isomorph_default.newTracingChannel (file:///usr/src/app/node_modules/braintrust/dist/chunk-XXXXXXXX.mjs:00000:00)
> 7: tracingChannel (file:///usr/src/app/node_modules/braintrust/dist/chunk-XXXXXXXX.mjs:000:00)
> 8: traceSyncStreamChannel (file:///usr/src/app/node_modules/braintrust/dist/chunk-XXXXXXXX.mjs:00000:00)
> ```
>
> **Cause:** Node's built-in `diagnostics_channel` module keeps a fixed-size table of tracing channels, capped at 1024 as of Node.js v26.5.0. Each auto-instrumentation registers channels in that table, so `kMaxChannels` is reached once too many instrumentations load. This is most common when Braintrust runs alongside another APM instrumentation such as Datadog, New Relic, or Sentry, which also register channels.
>
> **Fix:** Reduce the number of active instrumentations so the table stays under the limit. [Disable unused Braintrust integrations](https://docs.braintrust.dev/instrument/trace-llm-calls.md#auto-instrumentation) with `BRAINTRUST_DISABLE_INSTRUMENTATION`, a comma-separated list of integrations to skip, and disable unused instrumentations in your other APM agent.
>
> ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> BRAINTRUST_DISABLE_INSTRUMENTATION=openai,anthropic node --import braintrust/hook.mjs app.js
> ```


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