{"record":{"id":"781e92337bb30522","repo":"Tencent/WeKnora","slug":"download-w","errorCode":null,"errorMessage":"download: %w","messagePattern":"download: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/docparser/image_resolver.go","lineNumber":792,"sourceCode":"// two syntaxes.\nfunc fetchAndStoreRemoteImage(\n\tctx context.Context,\n\tclient *http.Client,\n\tfileSvc interfaces.FileService,\n\ttenantID uint64,\n\timgURL string,\n) (*remoteImageResult, error) {\n\twhitelisted := isWhitelistedImageHost(imgURL)\n\n\tif !whitelisted {\n\t\tif err := secutils.ValidateURLForSSRF(imgURL); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"blocked by SSRF policy: %w\", err)\n\t\t}\n\t}\n\n\tdata, mimeType, err := downloadImage(ctx, client, imgURL)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"download: %w\", err)\n\t}\n\n\tif isIconImage(data) {\n\t\treturn nil, errRemoteImageIsIcon\n\t}\n\n\tif whitelisted {\n\t\treturn &remoteImageResult{MimeType: mimeType, KeepOriginalURL: true}, nil\n\t}\n\n\text := extFromMime(mimeType)\n\tif ext == \"\" {\n\t\text = extFromURLPath(imgURL)\n\t}\n\tif ext == \"\" {\n\t\text = \".png\" // safe default\n\t}\n\tservingURL, err := fileSvc.SaveBytes(ctx, data, tenantID, uuid.New().String()+ext, false)","sourceCodeStart":774,"sourceCodeEnd":810,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/docparser/image_resolver.go#L774-L810","documentation":"This error wraps any failure from downloadImage while fetching a remote image, including request creation, HTTP transport errors, non-200 statuses, bad content types, oversized bodies, and read errors. It is the generic download stage failure in fetchAndStoreRemoteImage.","triggerScenarios":"downloadImage returns an error for the given URL: DNS failure, connection refused/timeout, non-200 status, non-image Content-Type, or body exceeding maxRemoteImageSize.","commonSituations":"Image host is down or slow, hotlink protection returning 403, dead image URLs in documents, images larger than the configured size cap, network egress blocked in the deployment environment.","solutions":["Unwrap the error to see the underlying cause (HTTP GET vs status vs content-type)","Verify the image URL is alive and publicly reachable (curl -I)","Check egress/proxy configuration and DNS in the deployment environment","Confirm the image is within maxRemoteImageSize and served with an image Content-Type"],"exampleFix":"// before\ndata, mimeType, err := downloadImage(ctx, client, imgURL)\nif err != nil { return nil, fmt.Errorf(\"download: %w\", err) }\n// after\ndata, mimeType, err := downloadImage(ctx, client, imgURL)\nif err != nil {\n    log.Warn(\"remote image download failed\", \"url\", imgURL, \"err\", err) // record URL for diagnosis\n    return nil, fmt.Errorf(\"download: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// preflight reachability and content type\nresp, err := http.Head(imgURL)\nif err != nil {\n    return fmt.Errorf(\"image unreachable: %w\", err)\n}\nif resp.StatusCode != 200 || !strings.HasPrefix(resp.Header.Get(\"Content-Type\"), \"image/\") {\n    return fmt.Errorf(\"not a fetchable image: %d %s\", resp.StatusCode, resp.Header.Get(\"Content-Type\"))\n}","typeGuard":null,"tryCatchPattern":"res, err := resolver.fetchAndStoreRemoteImage(ctx, imgURL, tenantID)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"download:\") {\n        // classify inner cause: SSRF/status/timeout/size\n        log.Warn(\"remote image skipped\", \"url\", imgURL, \"cause\", err)\n        return nil // skip image, keep processing document\n    }\n    return err\n}","preventionTips":["Preflight HEAD requests to validate image URLs before download","Set sensible timeouts on the SSRF-safe client","Handle hotlink-protected hosts with proper headers or skip them","Log the full wrapped chain to identify the failing stage"],"tags":["http","download","network","image"],"backgroundTag":"image-download-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}