{"record":{"id":"78451d0c60ade912","repo":"FiloSottile/age","slug":"stream-writer-is-already-closed","errorCode":null,"errorMessage":"stream.Writer is already closed","messagePattern":"stream\\.Writer is already closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/stream/stream.go","lineNumber":236,"sourceCode":"\t\t\t\treturn 0, err\n\t\t\t}\n\t\t}\n\t}\n\treturn total, nil\n}\n\n// Close flushes the last chunk. It does not close the underlying Writer.\nfunc (w *EncryptWriter) Close() error {\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\n\tw.err = w.flushChunk(lastChunk)\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\n\tw.err = errors.New(\"stream.Writer is already closed\")\n\treturn nil\n}\n\nconst (\n\tlastChunk    = true\n\tnotLastChunk = false\n)\n\nfunc (w *EncryptWriter) flushChunk(last bool) error {\n\tif !last && w.buf.Len() != ChunkSize {\n\t\tpanic(\"stream: internal error: flush called with partial chunk\")\n\t}\n\n\tif last {\n\t\tsetLastChunkFlag(&w.nonce)\n\t}\n\tw.buf.Grow(chacha20poly1305.Overhead)\n\tciphertext := w.a.Seal(w.buf.Bytes()[:0], w.nonce[:], w.buf.Bytes(), nil)","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/stream/stream.go#L218-L254","documentation":"Calling Close on a stream.Writer a second time returns this error (stored in w.err on first Close). After Close, the writer's internal error is poisoned so any further Write or Close fails. Note the first Close returns nil while setting this error for subsequent calls.","triggerScenarios":"Calling Close twice on the same *stream.Writer — e.g. explicit Close plus deferred Close, or Close inside both a flush routine and a defer.","commonSituations":"double-close via defer and explicit error-handling path; wrapping the writer in another closer that also closes it; reusing a writer object after close.","solutions":["Ensure Close is called exactly once, e.g. with a sync.Once or a bool closed flag.","If using defer w.Close(), do not also call Close on the success path.","Ignore the error if idempotent close semantics are desired: check err.Error() for \"already closed\" and treat as success."],"exampleFix":"// before\ndefer w.Close()\n...\nif err := w.Close(); err != nil { ... } // second close\n// after\nvar closeOnce sync.Once\ncloseFn := func() error {\n    var err error\n    closeOnce.Do(func() { err = w.Close() })\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"var closeOnce sync.Once\nclose := func() error {\n    var err error\n    closeOnce.Do(func() { err = w.Close() })\n    return err\n}\nif err := close(); err != nil && strings.Contains(err.Error(), \"already closed\") {\n    err = nil\n}","preventionTips":["Close stream.Writer exactly once — prefer a single defer without a duplicate explicit Close.","Never reuse a closed writer; create a new one for each stream.","Remember the first Close returns nil but poisons the writer."],"tags":["streaming","writer","lifecycle"],"backgroundTag":"writer-already-closed","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}