{"record":{"id":"5aec769af3f4e3b4","repo":"containerd/containerd","slug":"seek-on-closed-reader-w","errorCode":null,"errorMessage":"seek on closed reader: %w","messagePattern":"seek on closed reader: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/remotes/handlers.go","lineNumber":466,"sourceCode":"}\n\nfunc (c *closeOnEOFReader) Close() error {\n\tvar err error\n\tc.once.Do(func() {\n\t\tc.closed = true\n\t\terr = c.rc.Close()\n\t})\n\treturn err\n}\n\ntype closeOnEOFReadSeeker struct {\n\tcloseOnEOFReader\n\tseeker io.Seeker\n}\n\nfunc (c *closeOnEOFReadSeeker) Seek(offset int64, whence int) (int64, error) {\n\tif c.closed {\n\t\treturn 0, fmt.Errorf(\"seek on closed reader: %w\", io.ErrClosedPipe)\n\t}\n\treturn c.seeker.Seek(offset, whence)\n}\n","sourceCodeStart":448,"sourceCodeEnd":470,"githubUrl":"https://github.com/containerd/containerd/blob/4246446a2bf7d03837b0244118d858799393bd80/core/remotes/handlers.go#L448-L470","documentation":"The closeOnEOFReadSeeker wrapper refuses Seek calls after its underlying reader has been closed, returning this error wrapping io.ErrClosedPipe. It exists to prevent seeking on a reader that was already shut down (the wrapper closes itself on EOF).","triggerScenarios":"Calling Seek on a closeOnEOFReadSeeker after Close was invoked — typically when content verification code re-seeks a reader that already hit EOF and was closed (e.g. re-reading a response body for a second digest verification pass).","commonSituations":"Re-verifying fetched content twice; retry logic seeking the same reader after consumption; libraries expecting an open io.ReadSeeker but receiving one closed by the handler.","solutions":["Don't reuse or re-seek the reader after it reached EOF/closed; create a new fetch","If re-reading is needed, re-open the content (re-fetch the blob) instead of seeking back","Check caller logic for double verification passes on the same reader"],"exampleFix":"// before: reusing closed reader\n_, err = rs.Seek(0, io.SeekStart) // seek on closed reader\n// after: re-fetch instead\nresp, err := fetcher.Fetch(ctx, desc)\nrs := newCloseOnEOFReadSeeker(resp.Body)","handlingStrategy":"type-guard","validationCode":"// before seeking, confirm reader not closed\n// track close state yourself\n","typeGuard":"func seekable(rs io.ReadSeeker) bool {\n    type closedCheck interface{ Closed() bool }\n    if c, ok := rs.(closedCheck); ok {\n        return !c.Closed()\n    }\n    return true\n}","tryCatchPattern":"if _, err := rs.Seek(0, io.SeekStart); err != nil {\n    if errors.Is(err, io.ErrClosedPipe) {\n        // re-fetch content instead of reusing the closed reader\n        resp, err := fetcher.Fetch(ctx, desc)\n        if err != nil { return err }\n        rs = newCloseOnEOFReadSeeker(resp.Body)\n    }\n}","preventionTips":["Never reuse a reader after EOF-triggered close; re-fetch instead","Avoid double-verification passes on the same fetched reader","If re-reading is required, buffer the content in memory/disk first"],"tags":["containerd","io","seeker","reader"],"backgroundTag":"seek-on-closed-reader","analyzedSha":"4246446a2bf7d03837b0244118d858799393bd80","analyzedAt":"2026-09-02T00:14:43.053Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}