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
- Send avatarUrl as a base64 data URL string (data:<mime>;base64,<data>).
- Omit the avatarUrl key entirely if no avatar is intended.
- 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
- Type metadata.avatarUrl as string | null | undefined in client schemas.
- Validate payloads with zod before submitting agent create/update requests.
- Normalize form inputs to strings or null before sending.
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
- metadata.avatarUrl is empty
- bad request: ${responseText}
- ${label} must be a string.
- Rule metadata is too large.
- ${label} must be an object.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/c58dec5cc5d8a6fd.
Report an issue: GitHub.