{"record":{"id":"d529f35f294a6d27","repo":"siyuan-note/siyuan","slug":"generated-image-is-invalid","errorCode":null,"errorMessage":"generated image is invalid","messagePattern":"generated image is invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/openai.go","lineNumber":717,"sourceCode":"\t}\n\tif len(data) > maxGeneratedImageBytes {\n\t\treturn \"\", \"\", errors.New(\"generated image exceeds size limit\")\n\t}\n\tmimeType = mimetype.Detect(data).String()\n\tswitch mimeType {\n\tcase \"image/png\":\n\t\textension = \".png\"\n\tcase \"image/jpeg\":\n\t\textension = \".jpg\"\n\tcase \"image/webp\":\n\t\textension = \".webp\"\n\tdefault:\n\t\treturn \"\", \"\", fmt.Errorf(\"unsupported generated image type: %s\", mimeType)\n\t}\n\tconfig, _, decodeErr := image.DecodeConfig(bytes.NewReader(data))\n\tif decodeErr != nil || config.Width < 1 || config.Height < 1 || config.Width > 16384 || config.Height > 16384 ||\n\t\tint64(config.Width)*int64(config.Height) > maxGeneratedImagePixels {\n\t\treturn \"\", \"\", errors.New(\"generated image is invalid\")\n\t}\n\treturn mimeType, extension, nil\n}\n\nfunc NewOpenAIImageAdapter(apiKey, apiBaseURL, model string, timeout int) *OpenAIImageAdapter {\n\tif timeout < 1 {\n\t\ttimeout = 30\n\t}\n\treturn &OpenAIImageAdapter{\n\t\tclient:  NewOpenAIClientWithModel(apiKey, apiBaseURL, model),\n\t\tmodel:   model,\n\t\ttimeout: time.Duration(timeout) * time.Second,\n\t}\n}\n\nfunc (adapter *OpenAIImageAdapter) Generate(ctx context.Context, request GenerateImageRequest) (GeneratedImage, error) {\n\tif strings.TrimSpace(request.Prompt) == \"\" {\n\t\treturn GeneratedImage{}, errors.New(\"image prompt is required\")","sourceCodeStart":699,"sourceCodeEnd":735,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/openai.go#L699-L735","documentation":"Thrown by ValidateGeneratedImage (kernel/util/openai.go:717) after the MIME check passes but image.DecodeConfig fails, or the decoded dimensions are out of bounds: width/height must be 1..16384 and width*height must not exceed maxGeneratedImagePixels (100 * 1000 * 1000, ~100MP). It catches corrupt, truncated, or absurdly large images that nonetheless sniff as a valid PNG/JPEG/WebP.","triggerScenarios":"Bytes that pass mimetype detection but are truncated so image.DecodeConfig errors; a PNG/JPEG whose declared dimensions exceed 16384 on either side; a generated image whose total pixel count exceeds 100MP; a malformed header that the MIME sniffer accepted.","commonSituations":"Truncated download (connection dropped mid-body); provider returned a header-only stub; adversarial 'decompression bomb' style header; provider bug emitting a tiny valid header with no pixel data.","solutions":["Re-fetch or regenerate the image — corrupt payloads from signed CDN URLs are often transient.","If persistently failing, capture the bytes and run `file -k` / `identify` locally to confirm the header is well-formed.","Verify the model's configured Size does not exceed 16384 on either dimension.","If you control the source, ensure the encoder flushes the full file before returning."],"exampleFix":"// before\ndata, err := downloadGeneratedImage(requestCtx, result.URL)\nif err != nil { return GeneratedImage{}, err }\nmimeType, extension, err := ValidateGeneratedImage(data)\n\n// after (retry once on decode-class failures)\ndata, err := downloadGeneratedImage(requestCtx, result.URL)\nif err != nil { return GeneratedImage{}, err }\nif _, _, verr := ValidateGeneratedImage(data); verr != nil {\n    logging.LogWarnf(\"generated image invalid, retrying once: %s\", verr)\n    data, err = downloadGeneratedImage(requestCtx, result.URL)\n    if err != nil { return GeneratedImage{}, err }\n}\nmimeType, extension, err := ValidateGeneratedImage(data)","handlingStrategy":"retry","validationCode":"// Cheap pre-check: decode config yourself first, with a clearer failure path\ncfg, _, derr := image.DecodeConfig(bytes.NewReader(data))\nif derr != nil {\n    return fmt.Errorf(\"image header undecodable (truncated?): %s\", derr)\n}\nif cfg.Width < 1 || cfg.Height < 1 || cfg.Width > 16384 || cfg.Height > 16384 ||\n    int64(cfg.Width)*int64(cfg.Height) > 100_000_000 {\n    return errors.New(\"image dimensions out of accepted range\")\n}","typeGuard":null,"tryCatchPattern":"mimeType, ext, err := ValidateGeneratedImage(data)\nif err != nil && err.Error() == \"generated image is invalid\" {\n    // often transient (truncated download); retry once\n    data, derr := downloadGeneratedImage(ctx, url)\n    if derr == nil {\n        mimeType, ext, err = ValidateGeneratedImage(data)\n    }\n}\nif err != nil { return err }","preventionTips":["Retry downloads once — truncated bodies from signed CDN URLs are common.","Inspect the byte length and first bytes; a header without pixel data points to truncation.","Ensure the requested Size never exceeds 16384 on either dimension."],"tags":["openai","image-generation","validation","decode","go"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}