mastra-ai/mastra · error · HTTPException

metadata.avatarUrl must be a string

Error message

metadata.avatarUrl must be a string

What it means

HTTP 400 from validateMetadataAvatarUrl: when metadata contains an avatarUrl key, its value must be a string. Null/undefined are tolerated (treated as absent), but any non-string type (number, object, boolean) is rejected on agent create/update.

Source

Thrown at packages/server/src/server/handlers/validate-avatar.ts:14

import { HTTPException } from '../http-exception';

const AVATAR_MAX_BYTES = 512 * 1024; // 512 KB

/**
 * Validates `metadata.avatarUrl` if present.
 * Ensures it's a well-formed data URL and the decoded payload is ≤ 512 KB.
 * No-ops when metadata is absent or doesn't contain avatarUrl.
 */
export function validateMetadataAvatarUrl(metadata: Record<string, unknown> | undefined): void {
  if (!metadata || !('avatarUrl' in metadata) || metadata.avatarUrl === null || metadata.avatarUrl === undefined)
    return;
  if (typeof metadata.avatarUrl !== 'string') {
    throw new HTTPException(400, { message: 'metadata.avatarUrl must be a string' });
  }

  const dataUrl = metadata.avatarUrl;
  const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
  if (!match) {
    throw new HTTPException(400, {
      message: 'metadata.avatarUrl must be a valid data URL (data:<mime>;base64,<data>)',
    });
  }

  // `Buffer.from(..., 'base64')` decodes leniently — it silently ignores
  // invalid characters and never throws. Validate the payload format strictly
  // before measuring its byte length so malformed input is rejected.
  const base64Payload = match[2]!;
  const isStrictBase64 =
    base64Payload.length > 0 &&
    base64Payload.length % 4 === 0 &&
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(base64Payload);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Send avatarUrl as a base64 data URL string (data:<mime>;base64,<data>).
  2. Omit the avatarUrl key entirely if no avatar is intended.
  3. Add client-side type validation that avatarUrl is a string before submitting.

Example fix

// before
metadata: { avatarUrl: 42 }
// after
metadata: { avatarUrl: 'data:image/png;base64,iVBORw0KGgo...' }
Defensive patterns

Strategy: type-guard

Validate before calling

if (metadata && 'avatarUrl' in metadata && metadata.avatarUrl != null && typeof metadata.avatarUrl !== 'string') {
  throw new Error('avatarUrl must be a string data URL');
}

Type guard

function isValidAvatarUrl(v: unknown): v is string {
  return typeof v === 'string';
}

Try / catch

try {
  await saveAgent({ metadata });
} catch (e) {
  if (e instanceof HTTPException && e.status === 400 && e.message.includes('avatarUrl must be a string')) {
    fixAvatarType(metadata);
  } else throw e;
}

Prevention

When it happens

Trigger: POST/PUT to CREATE_STORED_AGENT_ROUTE or UPDATE_STORED_AGENT_ROUTE with metadata like { avatarUrl: 123 } or { avatarUrl: { url: '...' } }.

Common situations: Client-side forms sending numbers/null after JSON coercion; scripts assigning the result of a size calculation to avatarUrl; missing client-side schema validation on the metadata object.

Related errors


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