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

  1. Provide contentType on the attachment (e.g. 'audio/mpeg', 'application/json').
  2. If the data is actually text, ensure it is recognized as text (or set contentType 'text/plain') so it follows the text path.
  3. Sniff the MIME type server-side (e.g. file-type / magic bytes) before building the attachment.
  4. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/1196cbec13090a30. Report an issue: GitHub.