{"record":{"id":"6a6a194dc65c7574","repo":"golang/go","slug":"zip-setoffset-called-after-data-was-written","errorCode":null,"errorMessage":"zip: SetOffset called after data was written","messagePattern":"zip: SetOffset called after data was written","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/archive/zip/writer.go","lineNumber":59,"sourceCode":"\traw    bool\n}\n\n// NewWriter returns a new [Writer] writing a zip file to w.\n//\n// Note that the exact bytes written to w are not covered by the Go 1\n// compatibility promise. Callers, including tests, should not depend on the\n// exact written bytes.\nfunc NewWriter(w io.Writer) *Writer {\n\treturn &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}\n}\n\n// SetOffset sets the offset of the beginning of the zip data within the\n// underlying writer. It should be used when the zip data is appended to an\n// existing file, such as a binary executable.\n// It must be called before any data is written.\nfunc (w *Writer) SetOffset(n int64) {\n\tif w.cw.count != 0 {\n\t\tpanic(\"zip: SetOffset called after data was written\")\n\t}\n\tw.cw.count = n\n}\n\n// Flush flushes any buffered data to the underlying writer.\n// Calling Flush is not normally necessary; calling Close is sufficient.\nfunc (w *Writer) Flush() error {\n\treturn w.cw.w.(*bufio.Writer).Flush()\n}\n\n// SetComment sets the end-of-central-directory comment field.\n// It can only be called before [Writer.Close].\nfunc (w *Writer) SetComment(comment string) error {\n\tif len(comment) > uint16max {\n\t\treturn errors.New(\"zip: Writer.Comment too long\")\n\t}\n\tw.comment = comment\n\treturn nil","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/archive/zip/writer.go#L41-L77","documentation":"`zip.Writer.SetOffset(n)` records where the zip archive begins in the underlying stream, so that offsets in the central directory are correct when the zip is appended to a preamble (e.g. a self-extracting executable). It panics if any data has already been written to the writer, because adjusting the offset after bytes exist would corrupt those offsets. The check is `w.cw.count != 0`.","triggerScenarios":"Calling `Create`/`Write`/`Flush` before `SetOffset`; calling `SetOffset` after appending a file to the zip; reusing a Writer that already has buffered output.","commonSituations":"Self-extracting-executable builders that write the binary preamble first, then open a zip Writer but forget to `SetOffset(len(preamble))` before adding entries — or remember, but call it too late after a probe Write; refactors that reorder setup.","solutions":["Call `SetOffset(n)` immediately after `zip.NewWriter(w)` and before any `Create`/`Write`.","If you wrote a preamble to the same underlying writer, pass its exact length as the offset.","If you need to change the offset, discard the writer and create a fresh one."],"exampleFix":"// before\nzw := zip.NewWriter(w)\nzw.Create(\"a.txt\")          // writes header\nzw.SetOffset(preambleLen)  // panic\n// after\nzw := zip.NewWriter(w)\nzw.SetOffset(preambleLen)\nzw.Create(\"a.txt\")","handlingStrategy":"validation","validationCode":"zw := zip.NewWriter(w)\nzw.SetOffset(preambleLen) // must precede any Create/Write\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call SetOffset immediately after NewWriter when appending to a preamble.","Treat SetOffset as part of construction, not as a runtime setter."],"tags":["archive","zip","panic","api-ordering","self-extracting"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}