hcengineering/platform · warning · BadRequestError

Unsupported content type: ${contentType}

Error message

Unsupported content type: ${contentType}

What it means

findProvider scans the registered preview providers (image, doc, pdf, video, octet-stream, fallback) and returns the first whose supports(contentType) matches. If no provider supports the blob's content type, the service throws BadRequestError naming the type. It guards the provider dispatch layer from unknown MIME types.

Source

Thrown at pods/preview/src/service.ts:147

  @withContext('stat-blob')
  async statBlob (ctx: MeasureContext, workspace: WorkspaceUuid, name: string): Promise<Blob> {
    const wsId = { uuid: workspace } as any

    const stat = await this.storage.stat(ctx, wsId, name)
    if (stat !== undefined) {
      return stat
    }

    throw new NotFoundError()
  }

  findProvider (ctx: MeasureContext, contentType: string): PreviewProvider {
    const provider = this.providers.find((it) => it.supports(contentType))
    if (provider != null) {
      return provider
    }

    throw new BadRequestError(`Unsupported content type: ${contentType}`)
  }

  private imageKey (workspaceId: string, name: string): string {
    return `image/${workspaceId}/${name}`
  }

  private thumbnailKey (workspaceId: string, name: string, params: ThumbnailParams): string {
    return `thumbnail/${workspaceId}/${name}-${params.width}-${params.height}-${params.format}`
  }
}

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Log/inspect stat.contentType for the blob and compare against the providers' supports() rules.
  2. Fix the upload pipeline so a correct Content-Type is stored on the blob.
  3. Register or enable a provider (e.g. OctetStreamProvider or FallbackProvider) that covers the missing MIME type.
  4. Convert the file to a supported format (image, PDF, doc, video) before upload.

Example fix

// before
const provider = service.findProvider(ctx, stat.contentType)
// after
if (!isSupportedPreviewType(stat.contentType)) {
  return null // skip preview generation
}
const provider = service.findProvider(ctx, stat.contentType)
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED = ['image/', 'application/pdf', 'application/vnd.openxmlformats', 'application/msword', 'application/vnd.oasis.opendocument', 'video/']
if (!SUPPORTED.some((p) => stat.contentType.startsWith(p)) && stat.contentType !== 'application/octet-stream') {
  return null // unsupported preview type; skip
}

Type guard

function hasPreviewProvider(contentType: string): boolean {
  return ['image/','application/pdf','video/'].some((p) => contentType.startsWith(p)) ||
    contentType.includes('opendocument') || contentType.includes('openxmlformats') ||
    contentType === 'application/octet-stream' || contentType === 'application/msword'
}

Try / catch

try {
  return await preview.metadata(ctx, workspace, name)
} catch (err) {
  if (/Unsupported content type/.test(err.message)) {
    ctx.warn('no preview provider', { contentType: stat.contentType })
    return null
  }
  throw err
}

Prevention

When it happens

Trigger: Requesting metadata/thumbnail for a blob whose stored contentType matches none of the providers — e.g. an unusual MIME string, empty or misdetected contentType on upload, or a custom provider set that lacks a handler for the type.

Common situations: Uploads stored with wrong/missing contentType (client sent no Content-Type), exotic formats like .heic, .dwg, or application/octet-stream when the OctetStreamProvider is not registered, misconfigured storage recording 'undefined' as contentType.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/8ae0c5c414ffb342. Report an issue: GitHub.