fix: stop leaking a temp file on every image send#330
Conversation
SendImage created a temp file (os.CreateTemp) to hold the resized thumbnail, read it back, and only deferred Close() — never Remove() — so every image send left a file behind in TMPDIR, eventually exhausting disk on busy servers. The thumbnail is now resized and JPEG-encoded entirely in memory via the new jpegThumbnail helper (no temp file, two fewer filesystem ops). Test: TestJpegThumbnail (red->green). go build/vet/test pass on host and cross-compile for linux/amd64.
There was a problem hiding this comment.
Code Review
This pull request refactors the thumbnail generation in SendImage by introducing an in-memory helper function jpegThumbnail and an accompanying unit test, successfully eliminating the use of temporary files and preventing potential file leaks. The review feedback correctly points out that the img parameter in jpegThumbnail could be nil, which would lead to a runtime panic, and suggests adding a defensive nil check.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| func jpegThumbnail(img image.Image, width, height uint) ([]byte, error) { | ||
| thumb := resize.Thumbnail(width, height, img, resize.Lanczos3) | ||
| var buf bytes.Buffer | ||
| if err := jpeg.Encode(&buf, thumb, nil); err != nil { | ||
| return nil, err | ||
| } | ||
| return buf.Bytes(), nil | ||
| } |
There was a problem hiding this comment.
The img parameter is of interface type image.Image, which can be nil. If a nil image is passed to resize.Thumbnail, it will cause a runtime panic because resize.Thumbnail attempts to access the image's bounds. To ensure robust and defensive programming, add a nil check at the beginning of the function.
func jpegThumbnail(img image.Image, width, height uint) ([]byte, error) {
if img == nil {
return nil, errors.New("cannot create thumbnail from nil image")
}
thumb := resize.Thumbnail(width, height, img, resize.Lanczos3)
var buf bytes.Buffer
if err := jpeg.Encode(&buf, thumb, nil); err != nil {
return nil, err
}
return buf.Bytes(), nil
}There was a problem hiding this comment.
Addresses Gemini review: resize.Thumbnail dereferences the image bounds, so a nil image would panic. jpegThumbnail now returns an error for nil instead. Test: TestJpegThumbnailNil (fails — panics — without the guard, passes with it).
|
Thanks — added the nil guard. |
… path Adds TestJpegThumbnailRealImage: decodes the real static/images/background_image.png (1536x1024) exactly like SendImage does, runs jpegThumbnail, and asserts the output is byte-for-byte identical to the previous resize+encode (so the temp-file removal changed nothing but the mechanism) and is a valid, bounded JPEG.
What & why
SendImagecreated a temp file (os.CreateTemp) to hold the resized thumbnail, read it back, and only deferredClose()— neverRemove(). So every image send left a file behind inTMPDIR, eventually exhausting disk on busy servers.Change
The thumbnail is now resized and JPEG-encoded entirely in memory via a small
jpegThumbnailhelper — no temp file, and two fewer filesystem operations. Behavior is otherwise identical.Testing
TestJpegThumbnail— returns a decodable JPEG bounded by the requested size; fails on the un-resized path (red→green).go build ./...·go vet ./...·go test ./...— pass on host.GOOS=linux GOARCH=amd64 go build .+go test ./.../go test -race ./...— pass on linux/amd64 (Dockergolang:1.25).No API change — internal fix.