calcom/cal.diy · warning · HttpError

Unsupported Content-Type. Expected ${contentType}

Error message

Unsupported Content-Type. Expected ${contentType}

What it means

Thrown by parseRequestData (HttpError, HTTP 415 Unsupported Media Type) when the request Content-Type is neither application/json, application/x-www-form-urlencoded, nor multipart/form-data. The message echoes the unsupported content-type so the caller knows what was received vs expected.

Source

Thrown at apps/web/app/api/parseRequestData.ts:50

  if (contentType.includes("application/json")) {
    try {
      return await req.json();
    } catch (e) {
      log.error(`Invalid JSON: ${e} from path ${req.nextUrl}`);
      throw new HttpError({ statusCode: 400, message: "Bad Request (Invalid JSON)" });
    }
  }

  if (contentType.includes("application/x-www-form-urlencoded")) {
    return await parseUrlFormData(req);
  }

  if (contentType.includes("multipart/form-data")) {
    return await parseMultiFormData(req);
  }

  log.error(`Unsupported content type: ${contentType} from path ${req.nextUrl}`);
  throw new HttpError({ statusCode: 415, message: `Unsupported Content-Type. Expected ${contentType}` });
}

View on GitHub (pinned to 176037d0af)

Solutions

  1. Set Content-Type to one of: application/json, application/x-www-form-urlencoded, or multipart/form-data on the request.
  2. Default the client to application/json for structured payloads.
  3. If you genuinely need a new content-type, extend parseRequestData to handle it rather than relying on the fallback.

Example fix

// before
await fetch('/api/x', { method: 'POST', body: 'hello' }); // Content-Type defaults to text/plain

// after
await fetch('/api/x', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ msg: 'hello' }),
});
Defensive patterns

Strategy: validation

Validate before calling

// Send a supported Content-Type
const SUPPORTED = ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data'];
if (!SUPPORTED.some(t => contentType.startsWith(t))) {
  throw new Error(`Unsupported Content-Type: ${contentType}`);
}
await fetch('/api/x', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body });

Type guard

function isSupportedContentType(ct: string): boolean {
  return ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data']
    .some(t => ct.includes(t));
}

Try / catch

try {
  await parseRequestData(req);
} catch (e) {
  if (e instanceof HttpError && e.statusCode === 415) {
    return Response.json({ error: e.message }, { status: 415 });
  }
  throw e;
}

Prevention

When it happens

Trigger: POST/PUT with a Content-Type the dispatcher does not handle, e.g. text/plain, application/xml, an empty type, or a multipart type with an unexpected parameter.

Common situations: Client forgot to set Content-Type (defaults to text/plain in some fetch setups), sending XML/text to a JSON-only endpoint, custom content-type not yet supported, charset-only variations.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/ea417a2f926d1d81. Report an issue: GitHub.