wavetermdev/waveterm · error

ReadTailLines readLimit must be positive, got %d

Error message

ReadTailLines readLimit must be positive, got %d

What it means

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.

Source

Thrown at pkg/util/readutil/readutil.go:149

		return nil, false, err
	}

	lines, _, err := ReadLines(rs, linesToRead, 0, 0)
	if err != nil {
		return nil, false, err
	}

	return lines, hasMore, nil
}

// ReadTailLines reads the last lineCount lines from a file, excluding the last lineOffset lines.
// It progressively reads larger windows from the end of the file (starting at 1MB, doubling up to readLimit)
// until it finds enough lines or reaches the limit. Returns the lines, stop reason, and any error.
// Stop reason is StopReasonBOF when beginning of file is reached, StopReasonReadLimit when byte limit is reached,
// or empty string for natural completion (found requested line count).
func ReadTailLines(file *os.File, lineCount int, lineOffset int, readLimit int64) ([]string, string, error) {
	if readLimit <= 0 {
		return nil, "", fmt.Errorf("ReadTailLines readLimit must be positive, got %d", readLimit)
	}

	fileInfo, err := file.Stat()
	if err != nil {
		return nil, "", err
	}
	fileSize := fileInfo.Size()

	readBytes := int64(1024 * 1024)
	if readLimit < readBytes {
		readBytes = readLimit
	}

	for {
		startPos := fileSize - readBytes
		if startPos < 0 {
			startPos = 0
			readBytes = fileSize

View on GitHub (pinned to a4447c1563)

Solutions

  1. Pass a positive readLimit (e.g. 1MB default: 1<<20) at the call site
  2. Apply a default when config yields 0: if limit <= 0 { limit = defaultLimit }
  3. Check for arithmetic that can floor to zero before calling
  4. Validate the limit during config load so bad values fail early

Example fix

// before
lines, stop, err := readutil.ReadTailLines(f, 100, 0, cfg.MaxTailBytes) // may be 0
// after
limit := cfg.MaxTailBytes
if limit <= 0 {
	limit = 1 << 20 // 1 MiB default
}
lines, stop, err := readutil.ReadTailLines(f, 100, 0, limit)
Defensive patterns

Strategy: validation

Validate before calling

if readLimit <= 0 {
	readLimit = 1 << 20 // default 1 MiB
}
lines, stop, err := readutil.ReadTailLines(f, lineCount, lineOffset, readLimit)

Try / catch

lines, stop, err := readutil.ReadTailLines(f, n, off, limit)
if err != nil {
	if strings.Contains(err.Error(), "readLimit must be positive") {
		return fmt.Errorf("caller bug: invalid readLimit %d", limit)
	}
	return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/5d6faf0054687b7d. Report an issue: GitHub.