vercel/ai · error

Open Responses extension ID ${extension.id} must use <implem

Error message

Open Responses extension ID ${extension.id} must use <implementor>.<extension> format.

What it means

Open Responses extensions must be namespaced with an ID of the form '<implementor>.<extension>' so tool/item types cannot collide between implementors. createOpenResponsesExtensionRegistry throws when an extension id lacks a '.' separator (or starts with one), because no namespace can be derived.

Source

Thrown at packages/open-responses/src/open-responses-extension.ts:163

  eventTypes: readonly OpenResponsesNamespacedType[];
  decodeEvent: NonNullable<OpenResponsesExtension['decodeEvent']>;
};

export function createOpenResponsesExtensionRegistry(
  extensions?: readonly OpenResponsesExtension[],
): OpenResponsesExtensionRegistry {
  const registry: OpenResponsesExtensionRegistry = {
    byEventType: new Map(),
    byExtensionId: new Map(),
    byItemType: new Map(),
    byProviderToolId: new Map(),
    byToolType: new Map(),
  };

  for (const extension of extensions ?? []) {
    const namespaceSeparatorIndex = extension.id.indexOf('.');
    if (namespaceSeparatorIndex <= 0) {
      throw new Error(
        `Open Responses extension ID ${extension.id} must use <implementor>.<extension> format.`,
      );
    }
    const namespace = extension.id.slice(0, namespaceSeparatorIndex);

    registerUnique({
      map: registry.byExtensionId,
      key: extension.id,
      extension,
      field: 'id',
    });

    const hasToolType = extension.toolType != null;
    const hasToolEncoder = extension.encodeTool != null;
    if (hasToolType !== hasToolEncoder) {
      throw new Error(
        `Open Responses extension ${extension.id} must provide toolType and encodeTool together.`,
      );

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Change the id to include a dot: '<implementor>.<extension>' (e.g. 'acme.pdfAnnotation').
  2. Verify the id contains at least one '.' that is not the first character.
  3. Ensure the implementor prefix is stable/unique for your organization.

Example fix

// before
const ext = { id: 'pdfannotation', toolType: 'pdf', encodeTool: ... }
// after
const ext = { id: 'acme.pdfannotation', toolType: 'pdf', encodeTool: ... }
Defensive patterns

Strategy: validation

Validate before calling

function hasNamespacedId(ext: { id: string }): boolean {
  return ext.id.includes('.') && ext.id.indexOf('.') > 0;
}
extensions.forEach(e => { if (!hasNamespacedId(e)) throw new Error(`extension id '${e.id}' must be <implementor>.<extension>`); });

Type guard

function isNamespacedExtensionId(id: string): id is `${string}.${string}` {
  return id.indexOf('.') > 0;
}

Try / catch

try {
  const registry = createOpenResponsesExtensionRegistry(extensions);
} catch (e) {
  if (e instanceof Error && e.message.includes('must use <implementor>.<extension> format')) {
    // fix the offending extension id before re-creating the registry
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing an extension to createOpenResponses / the model constructor with id like 'myext', 'my-ext', or '.hidden' — i.e. indexOf('.') <= 0.

Common situations: Copy-pasting an extension example and renaming the id without keeping the dot; building a custom extension for internal use with a single-word id; typos like 'acme-pdf' intending a dot but using a dash.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/26768662a9bfef2a. Report an issue: GitHub.