{"record":{"id":"662ef0777121b145","repo":"JuliusBrussee/caveman","slug":"empty-render-text","errorCode":null,"errorMessage":"empty render text","messagePattern":"empty render text","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/pixel/transform_gemini.go","lineNumber":967,"sourceCode":"\t\t}\n\t}\n\treturn lastClosed\n}\n\nfunc maybeGeminiReflow(text string, enabled bool) string {\n\tif !enabled {\n\t\treturn text\n\t}\n\tsafe := NeutralizeSentinel(text)\n\tif packed, ok := Reflow(safe); ok {\n\t\treturn packed\n\t}\n\treturn safe\n}\n\nfunc renderGeminiInlineDataParts(model, mediaResolution, text string, cols, numCols int, shrinkWidth bool, maxCharsPerImage int, style RenderStyle) (geminiRenderResult, error) {\n\tif text == \"\" {\n\t\treturn geminiRenderResult{}, errors.New(\"empty render text\")\n\t}\n\tif cols <= 0 {\n\t\tcols = DefaultCols\n\t}\n\tif maxCharsPerImage <= 0 {\n\t\tmaxCharsPerImage = ReadableCharsPerImage\n\t}\n\teffectiveCols := cols\n\tif shrinkWidth {\n\t\teffectiveCols = MeasureContentCols(text, cols, 1)\n\t}\n\teffectiveNumCols := max(1, numCols)\n\tif effectiveCols < cols {\n\t\teffectiveNumCols = 1\n\t}\n\tvar imgs []RenderedImage\n\tvar err error\n\tif effectiveNumCols > 1 {","sourceCodeStart":949,"sourceCodeEnd":985,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/engine/pixel/transform_gemini.go#L949-L985","documentation":"renderGeminiInlineDataParts is the entry point that turns text into Gemini inlineData image parts; it rejects an empty input string up front because there is nothing to rasterize and returning zero parts would silently signal 'rendered fine'. Any caller must pass non-empty text.","triggerScenarios":"Calling the render function with \"\" after upstream text was fully stripped (NeutralizeSentinel/Reflow removed everything), when a message body is empty, or when a slice/text field defaults to empty before the call.","commonSituations":"Pipeline where compression or sentinel-neutralization reduces input to empty string; UI code rendering an optional field that is absent; tests invoking the renderer with placeholder empty strings.","solutions":["Guard the call site: skip rendering when strings.TrimSpace(text) == \"\"","Investigate why upstream produced empty text (over-aggressive stripping) if empty input is unexpected","Pass a meaningful placeholder only if the API contract allows non-empty stand-ins"],"exampleFix":"// before\nres, err := renderGeminiInlineDataParts(model, res0, text, cols, numCols, shrink, maxChars, style)\n\n// after\nif text == \"\" {\n    return nil // nothing to render\n}\nres, err := renderGeminiInlineDataParts(model, res0, text, cols, numCols, shrink, maxChars, style)","handlingStrategy":"validation","validationCode":"if text == \"\" {\n    return nil, nil // nothing to render\n}\nres, err := renderGeminiInlineDataParts(...)","typeGuard":"func renderable(t string) bool { return t != \"\" }","tryCatchPattern":null,"preventionTips":["Check for empty text after any stripping/compression stage, not just at intake","Log when text becomes empty unexpectedly — it usually signals over-aggressive filtering"],"tags":["pixel","go","gemini","render","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}