mastra-ai/mastra · error
If the attachment is not an image or text, it must specify a
Error message
If the attachment is not an image or text, it must specify a content type
What it means
For data: / inline (non-remote-URL) attachments, attachmentsToParts can handle images and text without an explicit type, but any other kind must carry a contentType to become a valid 'file' part. If contentType is missing for a non-image, non-text attachment, this Error is thrown.
Source
Thrown at packages/core/src/agent/message-list/prompt/attachments-to-parts.ts:81
break;
}
case 'data:': {
if (attachment.contentType?.startsWith('image/')) {
parts.push({
type: 'image',
image: urlString,
mimeType: attachment.contentType,
});
} else if (attachment.contentType?.startsWith('text/')) {
parts.push({
type: 'file',
data: urlString,
mimeType: attachment.contentType,
});
} else {
if (!attachment.contentType) {
throw new Error('If the attachment is not an image or text, it must specify a content type');
}
parts.push({
type: 'file',
data: urlString,
mimeType: attachment.contentType,
});
}
break;
}
default: {
throw new Error(`Unsupported URL protocol: ${url.protocol}`);
}
}
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Provide contentType on the attachment (e.g. 'audio/mpeg', 'application/json').
- If the data is actually text, ensure it is recognized as text (or set contentType 'text/plain') so it follows the text path.
- Sniff the MIME type server-side (e.g. file-type / magic bytes) before building the attachment.
- Reject attachments without a MIME type at your API boundary.
Example fix
// before
{ url: audioDataUrl } // no contentType, detected as binary
// after
{ url: audioDataUrl, contentType: 'audio/mpeg' } Defensive patterns
Strategy: validation
Validate before calling
function assertInlineAttachment(a: { url: string; contentType?: string }) {
const isImage = a.contentType?.startsWith('image/');
const isText = a.contentType?.startsWith('text/');
if (!isImage && !isText && !a.contentType) {
throw new Error(`Non-image/text attachment ${a.url.slice(0, 40)}… needs contentType`);
}
} Type guard
function isTypedAttachment(a: { url: string; contentType?: string }): a is { url: string; contentType: string } {
const img = a.contentType?.startsWith('image/');
const txt = a.contentType?.startsWith('text/');
return Boolean(img || txt || a.contentType);
} Try / catch
try {
parts = attachmentsToParts([attachment]);
} catch (e) {
if (e instanceof Error && e.message.includes('must specify a content type')) {
const sniffed = await sniffMimeType(attachment.data);
parts = attachmentsToParts([{ ...attachment, contentType: sniffed }]);
} else throw e;
} Prevention
- Sniff magic bytes server-side for base64/data: uploads missing MIME metadata.
- Reject binary uploads without a detected MIME type at intake.
- Label text payloads explicitly as text/* so they take the text path.
When it happens
Trigger: Adding an inline/data: attachment that is neither image/* nor text/* and lacks contentType — e.g. { url: 'data:application/octet-stream;base64,...' } with contentType omitted, or a binary attachment where MIME detection fails.
Common situations: Drag-and-drop uploads of arbitrary files where the client doesn't send a MIME type; binary blobs mislabeled as text; base64-encoded files reconstructed server-side without stored metadata.
Related errors
- If the attachment is not an image, it must specify a content
- Invalid URL: ${attachment.url}
- Unsupported URL protocol: ${url.protocol}
- Invalid \`mediaTypes\` pattern: ${JSON.stringify(pattern)}.
- ClaudeSDKAgent resumeData must include sessionId or continue
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1196cbec13090a30.
Report an issue: GitHub.