Tencent/WeKnora · error

non-image content type: %s

Error message

non-image content type: %s

What it means

This error is returned when the remote image's Content-Type header is neither an image/* type nor application/octet-stream. It is a safety check preventing storage of HTML error pages, text, or other non-image payloads as images.

Source

Thrown at internal/infrastructure/docparser/image_resolver.go:1034

	if err != nil {
		return nil, "", fmt.Errorf("HTTP GET: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, "", fmt.Errorf("unexpected status %d", resp.StatusCode)
	}

	// Determine MIME type from Content-Type header.
	ct := resp.Header.Get("Content-Type")
	mimeType, _, _ = mime.ParseMediaType(ct)
	if mimeType == "" {
		mimeType = "application/octet-stream"
	}

	// Only allow image content types (or octet-stream which we sniff later).
	if !strings.HasPrefix(mimeType, "image/") && mimeType != "application/octet-stream" {
		return nil, "", fmt.Errorf("non-image content type: %s", mimeType)
	}

	// Read body with size limit.
	limited := io.LimitReader(resp.Body, maxRemoteImageSize+1)
	body, err := io.ReadAll(limited)
	if err != nil {
		return nil, "", fmt.Errorf("read body: %w", err)
	}
	if len(body) > maxRemoteImageSize {
		return nil, "", fmt.Errorf("image exceeds %d bytes limit", maxRemoteImageSize)
	}

	// If MIME was octet-stream, sniff the real type from body.
	if mimeType == "application/octet-stream" {
		detected := http.DetectContentType(body)
		if strings.HasPrefix(detected, "image/") {
			mimeType = detected
		} else {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Verify the URL actually serves an image (curl -I to inspect Content-Type)
  2. Skip images whose host returns HTML error pages with 200 status
  3. If the server sends a wrong-but-known type, fix the server's Content-Type header
  4. Rely on octet-stream sniffing already implemented for servers omitting the type

Example fix

// before
if !strings.HasPrefix(mimeType, "image/") && mimeType != "application/octet-stream" {
    return nil, "", fmt.Errorf("non-image content type: %s", mimeType)
}
// after
// sniff first 512 bytes to rescue wrong headers
cmp := http.DetectContentType(body[:minInt(512, len(body))])
if !strings.HasPrefix(mimeType, "image/") && !strings.HasPrefix(cmp, "image/") {
    return nil, "", fmt.Errorf("non-image content type: %s", mimeType)
}
Defensive patterns

Strategy: validation

Validate before calling

// check Content-Type before downloading the full body
resp, err := http.Head(imgURL)
if err != nil { return err }
ct := resp.Header.Get("Content-Type")
if !strings.HasPrefix(ct, "image/") && ct != "application/octet-stream" {
    return fmt.Errorf("skipping non-image resource: %s", ct)
}

Try / catch

data, mimeType, err := downloadImage(ctx, client, remoteURL)
if err != nil {
    if strings.Contains(err.Error(), "non-image content type") {
        log.Warn("skipping non-image response", "url", remoteURL)
        return nil, "", errRemoteImageSkipped
    }
    return nil, "", err
}

Prevention

When it happens

Trigger: downloadImage receives a response whose Content-Type is e.g. text/html (soft-404/error page), application/json, or text/plain.

Common situations: Dead image links returning HTML error pages with 200 status, CDNs serving JSON error bodies, misconfigured servers sending wrong Content-Type headers, authenticated pages redirecting to login HTML.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/2d02ff27a32f903b. Report an issue: GitHub.