{"record":{"id":"934c694576fdf22f","repo":"Tencent/WeKnora","slug":"unexpected-status-d","errorCode":null,"errorMessage":"unexpected status %d","messagePattern":"unexpected status (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/docparser/image_resolver.go","lineNumber":1022,"sourceCode":"\n// downloadImage fetches an image from remoteURL using the provided SSRF-safe\n// client. It validates Content-Type and enforces maxRemoteImageSize.\nfunc downloadImage(ctx context.Context, client *http.Client, remoteURL string) (data []byte, mimeType string, err error) {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, remoteURL, nil)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"create request: %w\", err)\n\t}\n\t// Some CDNs require a browser-like User-Agent.\n\treq.Header.Set(\"User-Agent\", \"Mozilla/5.0 (compatible; WeKnora/1.0)\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"HTTP GET: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, \"\", fmt.Errorf(\"unexpected status %d\", resp.StatusCode)\n\t}\n\n\t// Determine MIME type from Content-Type header.\n\tct := resp.Header.Get(\"Content-Type\")\n\tmimeType, _, _ = mime.ParseMediaType(ct)\n\tif mimeType == \"\" {\n\t\tmimeType = \"application/octet-stream\"\n\t}\n\n\t// Only allow image content types (or octet-stream which we sniff later).\n\tif !strings.HasPrefix(mimeType, \"image/\") && mimeType != \"application/octet-stream\" {\n\t\treturn nil, \"\", fmt.Errorf(\"non-image content type: %s\", mimeType)\n\t}\n\n\t// Read body with size limit.\n\tlimited := io.LimitReader(resp.Body, maxRemoteImageSize+1)\n\tbody, err := io.ReadAll(limited)\n\tif err != nil {","sourceCodeStart":1004,"sourceCodeEnd":1040,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/docparser/image_resolver.go#L1004-L1040","documentation":"This error indicates the remote image server responded with an HTTP status other than 200 OK. The status code is embedded in the message so the caller can distinguish 403/404 (bad or protected URL) from 5xx (server-side failure).","triggerScenarios":"downloadImage receives a non-200 response: 404 for removed images, 403 for hotlink-protected resources, 429 rate limiting, 5xx from the image host.","commonSituations":"Hotlink protection rejecting the server's IP or Referer, expired/dead image links in documents, CDN rate limits, geographically blocked hosts.","solutions":["Read the status code: 404 means the image is gone — skip it; 403 means hotlink protection or IP block; 429 means back off and retry later","Use a normal browser-like User-Agent (already set) and consider a Referer header for hotlink-protected hosts","Verify the image URL directly with curl -I from the server","Treat 5xx as transient and retry with backoff"],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n    return nil, \"\", fmt.Errorf(\"unexpected status %d\", resp.StatusCode)\n}\n// after\nif resp.StatusCode == http.StatusNotFound {\n    return nil, \"\", errRemoteImageNotFound // sentinel callers can skip\n}\nif resp.StatusCode != http.StatusOK {\n    return nil, \"\", fmt.Errorf(\"unexpected status %d\", resp.StatusCode)\n}","handlingStrategy":"fallback","validationCode":"// preflight the image URL\nresp, err := http.Head(imgURL)\nif err != nil || resp.StatusCode != 200 {\n    status := 0\n    if resp != nil { status = resp.StatusCode }\n    return fmt.Errorf(\"image not fetchable: status=%d err=%v\", status, err)\n}","typeGuard":null,"tryCatchPattern":"data, mimeType, err := downloadImage(ctx, client, remoteURL)\nif err != nil {\n    if strings.Contains(err.Error(), \"unexpected status 404\") {\n        return nil, \"\", errRemoteImageNotFound // skip permanently missing images\n    }\n    if strings.Contains(err.Error(), \"unexpected status 4\") {\n        return nil, \"\", err // hotlink/forbidden: do not retry\n    }\n    return nil, \"\", err // 5xx: caller may retry\n}","preventionTips":["Skip 404 images permanently; retry only 5xx with backoff","Back off on 429 rate limits from CDNs","Send browser-like User-Agent/Referer for hotlink-protected hosts","Monitor which image hosts frequently return 403/404"],"tags":["http","status-code","image","cdn"],"backgroundTag":"http-403-forbidden","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}