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 audio content. Pass a string or a user message whose content contains only text parts.

What it means

convertHarnessPromptToACPTextBlocks throws unsupportedPromptContent when a file part's media type category is audio/*, since the ACP v1 harness supports only portable text prompts and cannot carry audio content to the agent.

Source

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

      content.push({ type: 'text', text: part.text });
      continue;
    }
    if (part.type === 'image') {
      throw unsupportedPromptContent({
        harnessId,
        category: 'image content',
      });
    }
    if (part.type === 'file') {
      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;
}

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Transcribe the audio to text locally (e.g. with a speech-to-text model) and send the transcript as a text part.
  2. Strip audio parts before sending the prompt.
  3. Use a harness/transport that supports audio if audio input is a hard requirement.

Example fix

// before
content: [{ type: 'file', mediaType: 'audio/wav', data }]
// after
content: [{ type: 'text', text: transcriptOfAudio }]
Defensive patterns

Strategy: validation

Validate before calling

const hasAudioFile = typeof prompt !== 'string' &&
  typeof prompt.content !== 'string' &&
  prompt.content.some(p => p.type === 'file' && p.mediaType.toLowerCase().startsWith('audio/'));
if (hasAudioFile) throw new Error('Transcribe audio to text before prompting an ACP v1 harness');

Type guard

function isAudioFilePart(part: unknown): boolean {
  const p = part as { type?: string; mediaType?: string };
  return p?.type === 'file' &&
    typeof p.mediaType === 'string' &&
    p.mediaType.toLowerCase().startsWith('audio/');
}

Try / catch

try {
  await session.prompt(msg);
} catch (e) {
  if (String(e?.message).includes('does not support audio content')) {
    await session.prompt(transcribeAudioParts(msg));
  } else throw e;
}

Prevention

When it happens

Trigger: Prompting an ACP v1 harness with a file part whose mediaType starts with 'audio/' (e.g. audio/wav, audio/mpeg), such as voice memos or transcribed-audio attachments.

Common situations: Voice-input chat features piping raw audio into an ACP coding-agent session.

Related errors


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