{"record":{"id":"11cbdb88520545b1","repo":"henrygd/beszel","slug":"s-returned-negative-bytes-d","errorCode":null,"errorMessage":"%s returned negative bytes: %d","messagePattern":"(.+?) returned negative bytes: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"agent/utils/utils.go","lineNumber":74,"sourceCode":"\treturn strings.TrimSpace(string(b)), true\n}\n\n// ReadStringFileLimited reads a file into a string with a maximum size (in bytes) to avoid\n// allocating large buffers and potential panics with pseudo-files when the size is misreported.\nfunc ReadStringFileLimited(path string, maxSize int) (string, error) {\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer f.Close()\n\n\tbuf := make([]byte, maxSize)\n\tn, err := f.Read(buf)\n\tif err != nil && err != io.EOF {\n\t\treturn \"\", err\n\t}\n\tif n < 0 {\n\t\treturn \"\", fmt.Errorf(\"%s returned negative bytes: %d\", path, n)\n\t}\n\treturn strings.TrimSpace(string(buf[:n])), nil\n}\n\n// FileExists reports whether the given path exists.\nfunc FileExists(path string) bool {\n\t_, err := os.Stat(path)\n\treturn err == nil\n}\n\n// ReadUintFile parses a decimal uint64 value from a file.\nfunc ReadUintFile(path string) (uint64, bool) {\n\traw, ok := ReadStringFileOK(path)\n\tif !ok {\n\t\treturn 0, false\n\t}\n\tparsed, err := strconv.ParseUint(raw, 10, 64)\n\tif err != nil {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/utils/utils.go#L56-L92","documentation":"ReadStringFileLimited reads up to maxSize bytes from the file at path and returns the trimmed contents. A negative read count violates the io.Reader contract (n < 0 should only accompany a non-nil error, which is already handled above), so the function defensively rejects it rather than returning corrupted string data.","triggerScenarios":"f.Read(buf) returns n < 0 with a nil or EOF error — indicative of a misbehaving custom io.Reader or a corrupted file handle; os.File.Read never legitimately does this.","commonSituations":"Extremely rare; seen when a test/mock reader is substituted for os.File, under FUSE or exotic filesystem quirks, or while fuzzing. Indicates a reader implementation bug rather than a caller mistake.","solutions":["Close and reopen the file and retry the read; transient file-handle state is the most plausible cause.","Check whether any custom io.Reader replaces os.File in this path and fix its Read to never return negative n.","Report upstream if reproducible with a plain os.File — this indicates a runtime or filesystem driver bug.","Log the raw (n, err) pair when this triggers to diagnose the offending reader."],"exampleFix":"// before\nn, err := f.Read(buf)\nif n < 0 {\n\treturn \"\", fmt.Errorf(\"%s returned negative bytes: %d\", path, n)\n}\n// after\nn, err := io.ReadFull(io.LimitReader(f, int64(maxSize)), buf)\nif err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {\n\treturn \"\", err\n} // n is never negative by construction","handlingStrategy":"validation","validationCode":"// prefer bounded, contract-safe reads and stat before reading\nif fi, err := os.Stat(path); err != nil || fi.Size() > int64(maxSize) {\n\treturn fmt.Errorf(\"file %s missing or too large\", path)\n}","typeGuard":"func validReadCount(n int) bool { return n >= 0 && n <= maxSize }","tryCatchPattern":"s, err := utils.ReadStringFileLimited(path, maxSize)\nif err != nil {\n\tif strings.Contains(err.Error(), \"returned negative bytes\") {\n\t\t// retry once with a fresh handle or fall back to os.ReadFile\n\t}\n\treturn err\n}","preventionTips":["Never substitute custom readers with broken Read implementations for os.File.","Use io.LimitReader/io.ReadFull for bounded reads.","Stat the file before reading to sanity-check size.","Report persistent occurrences as a runtime/filesystem bug."],"tags":["go","file-io","defensive-programming"],"backgroundTag":"invalid-read-count","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}