vercel/ai · error · HarnessCapabilityUnsupportedError

The ${harnessId} ACP harness supports only portable text pro

Error message

The ${harnessId} ACP harness supports only portable text prompts and does not support content parts of type ${JSON.stringify((part as { readonly type?: unknown }).type)}. Pass a string or a user message whose content contains only text parts.

What it means

Final catch-all in convertHarnessPromptToACPTextBlocks: any content part whose type is not 'text', 'image', or 'file' triggers unsupportedPromptContent with the unknown part type embedded in the message. The ACP v1 harness only accepts text content parts.

Source

Thrown at packages/harness-acp/src/v1/acp-v1-prompt.ts:59

      const mediaCategory = part.mediaType.toLowerCase().split('/', 1)[0];
      if (mediaCategory === 'image') {
        throw unsupportedPromptContent({
          harnessId,
          category: 'image content',
        });
      }
      if (mediaCategory === 'audio') {
        throw unsupportedPromptContent({
          harnessId,
          category: 'audio content',
        });
      }
      throw unsupportedPromptContent({
        harnessId,
        category: `embedded resource content with media type ${JSON.stringify(part.mediaType)}`,
      });
    }
    throw unsupportedPromptContent({
      harnessId,
      category: `content parts of type ${JSON.stringify(
        (part as { readonly type?: unknown }).type,
      )}`,
    });
  }
  return content;
}

export function prependACPInstructionGuidance({
  prompt,
  instructions,
}: {
  prompt: ReadonlyArray<ACPTextContentBlock>;
  instructions: string | undefined;
}): ACPTextContentBlock[] {
  const hasInstructions = instructions != null && instructions.length > 0;
  if (!hasInstructions) return [...prompt];

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Ensure prompt.content contains only text parts before calling the harness.
  2. Upgrade or align the SDK packages so part types match what HarnessV1 defines and ACP supports.
  3. Add a mapper that converts or drops non-text parts prior to prompting.

Example fix

// before
content: [...msg.content] // may include { type: 'tool-result', ... }
// after
content: msg.content.filter(p => p.type === 'text')
Defensive patterns

Strategy: validation

Validate before calling

const unknown = typeof prompt !== 'string' &&
  Array.isArray(prompt.content) &&
  prompt.content.filter(p => p.type !== 'text' && p.type !== 'image' && p.type !== 'file');
if (unknown.length) throw new Error(`Unknown content part types: ${unknown.map(p => p.type).join(', ')}`);

Type guard

function isKnownPart(part: unknown): boolean {
  const t = (part as { type?: unknown })?.type;
  return t === 'text' || t === 'image' || t === 'file';
}

Try / catch

try {
  await session.prompt(msg);
} catch (e) {
  if (String(e?.message).includes('content parts of type')) {
    await session.prompt({ content: msg.content.filter(isKnownPartAndTextOnly) });
  } else throw e;
}

Prevention

When it happens

Trigger: Prompting an ACP v1 harness with a content part of an unrecognized/unsupported type (e.g. 'tool-result', 'resource', or a part from a newer SDK version) inside prompt.content.

Common situations: SDK version mismatches introducing new part types; generic message-forwarding code passing arbitrary provider part types straight into the harness prompt.

Related errors


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