{"record":{"id":"5d6faf0054687b7d","repo":"wavetermdev/waveterm","slug":"readtaillines-readlimit-must-be-positive-got-d","errorCode":null,"errorMessage":"ReadTailLines readLimit must be positive, got %d","messagePattern":"ReadTailLines readLimit must be positive, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/readutil/readutil.go","lineNumber":149,"sourceCode":"\t\treturn nil, false, err\n\t}\n\n\tlines, _, err := ReadLines(rs, linesToRead, 0, 0)\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\n\treturn lines, hasMore, nil\n}\n\n// ReadTailLines reads the last lineCount lines from a file, excluding the last lineOffset lines.\n// It progressively reads larger windows from the end of the file (starting at 1MB, doubling up to readLimit)\n// until it finds enough lines or reaches the limit. Returns the lines, stop reason, and any error.\n// Stop reason is StopReasonBOF when beginning of file is reached, StopReasonReadLimit when byte limit is reached,\n// or empty string for natural completion (found requested line count).\nfunc ReadTailLines(file *os.File, lineCount int, lineOffset int, readLimit int64) ([]string, string, error) {\n\tif readLimit <= 0 {\n\t\treturn nil, \"\", fmt.Errorf(\"ReadTailLines readLimit must be positive, got %d\", readLimit)\n\t}\n\n\tfileInfo, err := file.Stat()\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tfileSize := fileInfo.Size()\n\n\treadBytes := int64(1024 * 1024)\n\tif readLimit < readBytes {\n\t\treadBytes = readLimit\n\t}\n\n\tfor {\n\t\tstartPos := fileSize - readBytes\n\t\tif startPos < 0 {\n\t\t\tstartPos = 0\n\t\t\treadBytes = fileSize","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/util/readutil/readutil.go#L131-L167","documentation":"ReadTailLines reads backwards from the end of a file in doubling windows up to readLimit bytes; a non-positive readLimit makes the window-growing algorithm meaningless, so it validates upfront and returns this error. It is an argument-contract error, not an I/O failure.","triggerScenarios":"Calling ReadTailLines(file, lineCount, lineOffset, 0) or with a negative readLimit — typically a zero-value int64 variable, a config that computed the limit incorrectly, or an uninitialized struct field passed as readLimit.","commonSituations":"Config parsing producing 0 for unset byte limits; integer division truncating a limit to 0; copying example code and omitting the limit argument; defaults not applied before the call.","solutions":["Pass a positive readLimit (e.g. 1MB default: 1<<20) at the call site","Apply a default when config yields 0: if limit <= 0 { limit = defaultLimit }","Check for arithmetic that can floor to zero before calling","Validate the limit during config load so bad values fail early"],"exampleFix":"// before\nlines, stop, err := readutil.ReadTailLines(f, 100, 0, cfg.MaxTailBytes) // may be 0\n// after\nlimit := cfg.MaxTailBytes\nif limit <= 0 {\n\tlimit = 1 << 20 // 1 MiB default\n}\nlines, stop, err := readutil.ReadTailLines(f, 100, 0, limit)","handlingStrategy":"validation","validationCode":"if readLimit <= 0 {\n\treadLimit = 1 << 20 // default 1 MiB\n}\nlines, stop, err := readutil.ReadTailLines(f, lineCount, lineOffset, readLimit)","typeGuard":null,"tryCatchPattern":"lines, stop, err := readutil.ReadTailLines(f, n, off, limit)\nif err != nil {\n\tif strings.Contains(err.Error(), \"readLimit must be positive\") {\n\t\treturn fmt.Errorf(\"caller bug: invalid readLimit %d\", limit)\n\t}\n\treturn err\n}","preventionTips":["Clamp limits from config to a positive minimum at load time","Beware zero-value int64 fields passed as limits","Watch for division/flooring arithmetic that can yield 0","Document that readLimit is in bytes and must be > 0"],"tags":["validation","argument-contract","file-io"],"backgroundTag":"invalid-argument-value","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}