Skip to content

Chapter 13 · Telemetry and Error Handling ​

Chapter goals:

  • Understand AI SDK's OpenTelemetry-based telemetry system and learn to enable and customize it
  • Master registerTelemetry global registration and per-call telemetry parameter configuration
  • Learn to write custom Telemetry integrations to collect generation lifecycle events
  • Master three error handling patterns:普通 errors, streaming errors, and stream abort (abort)

13.1 Telemetry Overview ​

AI SDK uses OpenTelemetry to collect telemetry data. OpenTelemetry is an open-source observability framework that provides standardized instrumentation specs for collecting telemetry.

You can check the official docs' AI SDK Observability Integrations page to see which vendors provide monitoring and tracing capabilities for AI SDK applications.

13.2 Enabling Telemetry ​

Step 1: Register the OpenTelemetry Integration ​

OpenTelemetry span collection requires installing the @ai-sdk/otel package and registering it once at application startup:

bash
pnpm install @ai-sdk/otel
ts
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';

registerTelemetry(new OpenTelemetry());

For Next.js applications, create an instrumentation.ts file at the project root and register both the AI SDK telemetry integration and the OpenTelemetry provider:

ts
import { registerOTel } from '@vercel/otel';
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';

export function register() {
  registerOTel({
    serviceName: 'my-ai-app',
  });

  registerTelemetry(new OpenTelemetry());
}

For more details on setting up OpenTelemetry in Next.js, see the Next.js official OpenTelemetry guide. For Node.js apps without Next.js, register at the top level of the entry file.

Step 2: Enable Telemetry ​

Once a telemetry integration is registered, all AI SDK calls emit telemetry events by default. You can still pass the telemetry parameter to attach metadata (e.g., functionId) or opt out of a specific call.

ts
import { generateText, createGateway } from 'ai';
import 'dotenv/config';

// Method 1: Vercel AI Gateway (recommended default); also applicable to custom providers (see Chapter 3)
const gateway = createGateway({ apiKey: process.env.AI_GATEWAY_API_KEY ?? '' });

const result = await generateText({
  model: gateway('openai/gpt-5'),
  prompt: 'Write a short story about a cat.',
  telemetry: {
    functionId: 'story-agent',
  },
});

By default, both input and output are recorded. Setting recordInputs and recordOutputs to false disables them — this helps with privacy, data transfer volume, and performance. For example, when input contains sensitive information, you can disable input recording only.

Opting Out ​

Telemetry is opt-out (enabled by default). To disable telemetry for a specific call, set isEnabled: false:

ts
const result = await generateText({
  model: gateway('openai/gpt-5'),
  prompt: 'Write a short story about a cat.',
  telemetry: { isEnabled: false },
});

13.3 Telemetry Metadata ​

You can provide a functionId to identify which function the telemetry data corresponds to, and attach additional info to telemetry via runtimeContext:

ts
const result = await generateText({
  model: gateway('openai/gpt-5'),
  prompt: 'Write a short story about a cat.',
  runtimeContext: {
    userId: 'user_123',
    requestId: 'req_abc',
  },
  telemetry: {
    functionId: 'my-awesome-function',
  },
});

Controlling Runtime Context in Telemetry ​

runtimeContext may contain values that are useful internally but should not be sent to telemetry vendors (e.g., user IDs, tenant IDs, or credentials). Use telemetry.includeRuntimeContext to mark which top-level attributes are allowed into telemetry:

ts
const result = await generateText({
  model: gateway('openai/gpt-5'),
  prompt: 'Write a short story about a cat.',
  runtimeContext: {
    userId: 'user_123',
    requestId: 'req_abc',
  },
  telemetry: {
    includeRuntimeContext: {
      requestId: true,
    },
  },
});

In this例, the telemetry integration receives only { requestId: 'req_abc' } in runtimeContext; attributes set to false or omitted are excluded. If telemetry.includeRuntimeContext is fully omitted, no runtime context attributes are included. This option is supported by generateText, streamText, and ToolLoopAgent.

Note: telemetry.includeRuntimeContext only filters telemetry integrations. Lifecycle callbacks and returned results still receive the full runtimeContext.

Tool Context ​

Tool context may similarly contain values that are useful during execution but should not enter telemetry (e.g., API keys, access tokens). Use telemetry.includeToolsContext to selectively include top-level attributes of the tool context:

ts
const weatherTool = tool({
  inputSchema: z.object({
    location: z.string(),
  }),
  contextSchema: z.object({
    weatherApiKey: z.string(),
    defaultUnit: z.enum(['celsius', 'fahrenheit']),
  }),
  execute: async ({ location }, { context }) =>
    fetchWeather(location, context.weatherApiKey, context.defaultUnit),
});

const result = await generateText({
  model: gateway('openai/gpt-5'),
  tools: { weather: weatherTool },
  toolsContext: {
    weather: {
      weatherApiKey: 'weather-123',
      defaultUnit: 'fahrenheit',
    },
  },
  prompt: 'What is the weather in San Francisco?',
  telemetry: {
    includeToolsContext: {
      weather: {
        defaultUnit: true,
      },
    },
  },
});

In this例, the toolContext in telemetry events will be { defaultUnit: 'fahrenheit' }; weatherApiKey is excluded. The rules are the same as runtimeContext: set to false or omit to exclude; omitting the entire option excludes all tool context attributes.

13.4 Custom Tracer ​

If you want traces to use a TracerProvider other than the @opentelemetry/api singleton, pass a custom Tracer to the OpenTelemetry constructor:

ts
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
import { NodeTracerProvider } from '@opentelemetry/sdk-node';

const tracerProvider = new NodeTracerProvider();
registerTelemetry(
  new OpenTelemetry({
    tracer: tracerProvider.getTracer('gen_ai'),
  }),
);

13.5 Error Handling ​

AI SDK has three classes of errors:

###普通 Errors

Thrown synchronously or as rejected promises. Handle with try/catch:

ts
try {
  const result = await generateText({ model, prompt: '...' });
} catch (error) {
  if (error instanceof OutputParserError) {
    // Handle structured output parsing failure
  } else if (error instanceof APIConnectionError) {
    // Handle connection errors
  } else {
    // Handle other errors
  }
}

Streaming Errors ​

In streamText, errors are emitted as parts in the stream. Handle them in the stream loop:

ts
const { stream } = streamText({ model, prompt: '...' });

for await (const part of stream) {
  if (part.type === 'error') {
    console.error('Stream error:', part.error);
    break;
  }
  // ... handle other parts
}

Stream Abort ​

Use AbortSignal to abort a streaming call:

ts
const controller = new AbortController();

const { stream } = streamText({
  model,
  prompt: '...',
  abortSignal: controller.signal,
});

// Abort after 5 seconds
setTimeout(() => controller.abort(), 5000);

for await (const part of stream) {
  // ...
}

When aborted, the stream emits an aborted part and the operation is cancelled.

Chapter Summary ​

  • AI SDK uses OpenTelemetry for telemetry; register @ai-sdk/otel once at startup;
  • registerTelemetry() globally registers the integration; per-call telemetry parameter adds metadata or opts out;
  • includeRuntimeContext and includeToolsContext let you selectively exclude sensitive attributes from telemetry;
  • Three error patterns:普通 errors (try/catch), streaming errors (handle error parts), and abort (use AbortSignal);
  • Custom TracerProvider can be passed to the OpenTelemetry constructor.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. Where should registerTelemetry be called?

2. What does telemetry.includeRuntimeContext do?

3. How do you handle errors in a streamText call?

4. What happens when you abort a stream with AbortSignal?

🛠️ Hands-On Exercises ​

  1. Register the OpenTelemetry integration in a Node.js app; call generateText and verify spans appear in your telemetry backend (e.g., Jaeger or Grafana Tempo).
  2. Use includeRuntimeContext to send only requestId (not userId) to telemetry; verify the excluded attribute does not appear in spans.
  3. Test stream abort: start a long streamText call, abort after 2 seconds, and verify the aborted part is emitted.

Next: Chapter 14 · Image Generation and Multimodal Input