zitadel/zitadel · error

invalid content-type: %s

Error message

invalid content-type: %s

What it means

The asset upload handler sniffs the uploaded file's MIME type and checks it against the uploader's allowed content types (ContentTypeAllowed). If the detected type is not allowed, the request is rejected with 400 'invalid content-type'.

Source

Thrown at internal/api/assets/asset.go:176

		defer func() {
			err = file.Close()
			logging.OnError(err).Warn("could not close file")
		}()

		mimeType, err := mimetype.DetectReader(file)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		_, err = file.Seek(0, io.SeekStart)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		size := handler.Size
		if !uploader.ContentTypeAllowed(mimeType.String()) {
			s.ErrorHandler()(w, r, fmt.Errorf("invalid content-type: %s", mimeType), http.StatusBadRequest)
			return
		}
		if size > uploader.MaxFileSize() {
			s.ErrorHandler()(w, r, fmt.Errorf("file too big, max file size is %vKB", uploader.MaxFileSize()/1024), http.StatusBadRequest)
			return
		}

		resourceOwner := uploader.ResourceOwner(authz.GetInstance(ctx), ctxData)
		objectName, err := uploader.ObjectName(ctxData)
		if err != nil {
			s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
			return
		}
		uploadInfo := &command.AssetUpload{
			ResourceOwner: resourceOwner,
			ObjectName:    objectName,
			ContentType:   mimeType.String(),
			ObjectType:    uploader.ObjectType(),

View on GitHub (pinned to 13948f2bcd)

Solutions

  1. Upload a file whose MIME type matches the endpoint's allowed list (e.g. PNG/JPEG for logos)
  2. Send a proper multipart/form-data request with the file part
  3. Check the specific uploader's allowed content types (e.g. internal/api/assets) and convert/serve the asset accordingly

Example fix

// before
curl -F "file=logo.svg" $HOST/assets/v1/instance
// after
convert logo.svg logo.png
curl -F "file=@logo.png" $HOST/assets/v1/instance
Defensive patterns

Strategy: validation

Validate before calling

const allowed = ["image/png", "image/jpeg", "image/webp"];
const file = formData.get("file");
if (!allowed.includes(file.type)) throw new Error(`content-type ${file.type} not allowed; use one of ${allowed.join(", ")}`);

Type guard

function isAllowedMime(t, allowed) { return allowed.includes(t); }

Try / catch

try {
  await uploadAsset(file);
} catch (e) {
  if (e.status === 400 && e.message.includes("invalid content-type")) {
    // convert the file to an allowed type and retry once
  }
}

Prevention

When it happens

Trigger: Uploading an asset (logo, icon, user avatar via /assets/v1 endpoints) whose detected MIME type is not in the uploader's allowed list, e.g. uploading an SVG to an endpoint that only accepts PNG/JPEG, or sending a text file where an image is required.

Common situations: Uploading files with wrong extensions after renaming; brand logos supplied as SVG when the endpoint only permits raster images; API clients sending JSON instead of multipart file data so the sniffed type is wrong.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of zitadel/zitadel@13948f2bcd (2026-09-06). Data as JSON: /api/errors/844d8bef1f0be332. Report an issue: GitHub.