{"record":{"id":"b46d2dd778f1fcd8","repo":"siyuan-note/siyuan","slug":"generated-image-is-empty","errorCode":null,"errorMessage":"generated image is empty","messagePattern":"generated image is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/openai.go","lineNumber":698,"sourceCode":"\tif err = jpeg.Encode(&output, decoded, &jpeg.Options{Quality: 92}); err != nil {\n\t\treturn PreparedImage{}, errors.New(\"encode image failed: \" + err.Error())\n\t}\n\tif maxBytes > 0 && output.Len() > maxBytes {\n\t\treturn PreparedImage{}, fmt.Errorf(\"prepared image exceeds size limit: %d bytes\", maxBytes)\n\t}\n\treturn PreparedImage{\n\t\tData:       output.Bytes(),\n\t\tMIMEType:   \"image/jpeg\",\n\t\tWidth:      bounds.Dx(),\n\t\tHeight:     bounds.Dy(),\n\t\tSourceSize: len(data),\n\t}, nil\n}\n\n// ValidateGeneratedImage 校验生成图片的格式、尺寸和体积。\nfunc ValidateGeneratedImage(data []byte) (mimeType, extension string, err error) {\n\tif len(data) == 0 {\n\t\treturn \"\", \"\", errors.New(\"generated image is empty\")\n\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 {","sourceCodeStart":680,"sourceCodeEnd":716,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/openai.go#L680-L716","documentation":"Thrown by ValidateGeneratedImage (kernel/util/openai.go:698) when the byte slice passed in has length 0. It is the first guard in the AI-generated image validation pipeline and exists because downstream MIME detection (mimetype.Detect) and image.DecodeConfig both misbehave or panic-prone on empty input. SiYuan runs it on every image returned by an OpenAIImageAdapter before persisting it.","triggerScenarios":"Calling ValidateGeneratedImage([]byte{}) directly; or OpenAIImageAdapter.Generate receiving a result whose B64JSON decoded to zero bytes, or whose URL returned HTTP 200 with an empty body. Also reachable if a caller wraps an upstream API whose 'data' field is present but empty.","commonSituations":"The model's response was filtered/empty but still returned 200 OK; a transient CDN issue served a 0-byte body; a base64 string decoded cleanly to nothing (e.g. provider sent \"\"); an integration bug truncated the byte buffer before validation.","solutions":["Inspect the upstream model response: log result.B64JSON length and the HTTP status / Content-Length of the downloaded URL to find where the bytes were lost.","Guard the call site: only invoke ValidateGeneratedImage when len(data) > 0, otherwise short-circuit with a clearer upstream error.","If using URL mode, retry the download once — empty bodies from signed CDN URLs are often transient.","Verify the openai-go client and model endpoint actually populate response.Data[0].B64JSON / .URL."],"exampleFix":"// before\ndata, err := base64.StdEncoding.DecodeString(result.B64JSON)\nif err != nil { return GeneratedImage{}, err }\nmimeType, ext, err := ValidateGeneratedImage(data)\n\n// after\ndata, err := base64.StdEncoding.DecodeString(result.B64JSON)\nif err != nil { return GeneratedImage{}, err }\nif len(data) == 0 {\n    return GeneratedImage{}, fmt.Errorf(\"model %s returned empty image payload\", adapter.model)\n}\nmimeType, ext, err := ValidateGeneratedImage(data)","handlingStrategy":"validation","validationCode":"// Run before ValidateGeneratedImage to short-circuit empty payloads\nfunc hasImageBytes(data []byte) bool { return len(data) > 0 }\n\n// usage\nif !hasImageBytes(data) {\n    return GeneratedImage{}, errors.New(\"upstream model returned no image bytes\")\n}\nmimeType, ext, err := ValidateGeneratedImage(data)","typeGuard":null,"tryCatchPattern":"mimeType, ext, err := ValidateGeneratedImage(data)\nif err != nil {\n    // err may be \"generated image is empty\"; log len(data) for diagnosis\n    logging.LogWarnf(\"validate generated image failed: %s (len=%d)\", err, len(data))\n    return err\n}","preventionTips":["Always check len(data) > 0 immediately after base64-decoding or downloading image bytes.","Log the length of result.B64JSON and the HTTP status/Content-Length of any URL response before validation.","When wiring a new image provider, write a contract test that asserts a non-empty payload on a fixed prompt."],"tags":["openai","image-generation","validation","go"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}