{"record":{"id":"87dd440f2fba5cf6","repo":"FiloSottile/age","slug":"failed-to-compute-stream-overhead-w","errorCode":null,"errorMessage":"failed to compute stream overhead: %w","messagePattern":"failed to compute stream overhead: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/inspect/inspect.go","lineNumber":94,"sourceCode":"\t// (which can have LF or CRLF line endings, varying its size), read to\n\t// the end to determine it.\n\tif fileSize == -1 || data.Armor {\n\t\tn, err := io.Copy(io.Discard, rest)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to read rest of file: %w\", err)\n\t\t}\n\t\tfileSize = data.Sizes.Header + n\n\t\tif !tr.done {\n\t\t\tpanic(\"trackReader not done after io.Copy\")\n\t\t}\n\t\tif tr.count != fileSize && !data.Armor {\n\t\t\tpanic(\"trackReader count mismatch\")\n\t\t}\n\t\tdata.Sizes.Armor = tr.count - fileSize\n\t}\n\tdata.Sizes.Overhead, err = streamOverhead(fileSize - data.Sizes.Header)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to compute stream overhead: %w\", err)\n\t}\n\tdata.Sizes.MinPayload = fileSize - data.Sizes.Header - data.Sizes.Overhead\n\tdata.Sizes.MaxPayload = data.Sizes.MinPayload\n\treturn data, nil\n}\n\ntype trackReader struct {\n\tr     io.Reader\n\tcount int64\n\tdone  bool\n}\n\nfunc (tr *trackReader) Read(p []byte) (int, error) {\n\tif tr.done {\n\t\treturn 0, io.EOF\n\t}\n\tn, err := tr.r.Read(p)\n\ttr.count += int64(n)","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/inspect/inspect.go#L76-L112","documentation":"Inspect computes the streaming overhead (nonce + chunk frames) from the encrypted payload size via streamOverhead, which calls stream.PlaintextSize. If the payload is smaller than the 16-byte stream nonce or the size is inconsistent with STREAM framing, this error is returned wrapped.","triggerScenarios":"inspect.Inspect calls streamOverhead(fileSize - data.Sizes.Header) and either payloadSize < 16 or stream.PlaintextSize rejects the encrypted size (e.g., size not congruent with chunk + 16-byte tag framing).","commonSituations":"Inspecting truncated or corrupted age files; files that are shorter than a single chunk plus nonce; non-age data that got past the header by coincidence; arithmetic on wrong fileSize values passed by the caller.","solutions":["Verify the file is complete and its declared size is correct","Re-transfer the file; a payload smaller than 16 bytes cannot be a valid age payload","Double-check the fileSize argument passed to Inspect (use the real file size; -1 to auto-detect)","Confirm the file was produced by age and not another tool"],"exampleFix":"// before\ninspect.Inspect(f, size) // size miscounted from armor\n// after\nst, _ := f.Stat()\ninspect.Inspect(f, st.Size()) // let Inspect detect armor and size itself","handlingStrategy":"validation","validationCode":"st, _ := f.Stat()\nhdrOverhead := int64(200) // generous minimum header size\nif st.Size() < hdrOverhead+16 { return fmt.Errorf(\"file too small to contain a valid age payload\") }","typeGuard":"n/a — stream.PlaintextSize returns an error rather than a type mismatch; check it with errors.Is","tryCatchPattern":"data, err := inspect.Inspect(f, size)\nif err != nil {\n    if strings.Contains(err.Error(), \"stream overhead\") { return fmt.Errorf(\"payload size inconsistent with age STREAM framing\") }\n    return err\n}","preventionTips":["Pass the true file size (or -1) to Inspect — never the size of an outer compressed container","Reject files smaller than header + 16 bytes before inspection","Verify completeness of truncated downloads before analysis"],"tags":["inspect","stream-framing","size-validation"],"backgroundTag":"payload-too-small","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}