golang/go · error

bytes.Reader.Seek: negative position

Error message

bytes.Reader.Seek: negative position

What it means

bytes.Reader.Seek returns this error when the computed absolute position would be negative (reader.go:129-131). After resolving offset against whence (start/current/end), the resulting position must be >= 0; a negative position is impossible for a forward-only byte slice, so the Reader rejects it. prevRune has already been cleared by this point.

Source

Thrown at src/bytes/reader.go:130

	return nil
}

// Seek implements the [io.Seeker] interface.
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
	r.prevRune = -1
	var abs int64
	switch whence {
	case io.SeekStart:
		abs = offset
	case io.SeekCurrent:
		abs = r.i + offset
	case io.SeekEnd:
		abs = int64(len(r.s)) + offset
	default:
		return 0, errors.New("bytes.Reader.Seek: invalid whence")
	}
	if abs < 0 {
		return 0, errors.New("bytes.Reader.Seek: negative position")
	}
	r.i = abs
	return abs, nil
}

// WriteTo implements the [io.WriterTo] interface.
func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
	r.prevRune = -1
	if r.i >= int64(len(r.s)) {
		return 0, nil
	}
	b := r.s[r.i:]
	m, err := w.Write(b)
	if m > len(b) {
		panic("bytes.Reader.WriteTo: invalid Write count")
	}
	r.i += int64(m)
	n = int64(m)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Clamp the computed absolute position to >= 0 before calling Seek, or compute offset so abs cannot go negative.
  2. For relative backward seeks, check the current position (via a prior Seek(0, io.SeekCurrent)) and bound n to it.
  3. Validate offsets originating from user input (HTTP Range, cursors) and reject negative start explicitly.
  4. Add a test for backward-seek edge cases near position 0.

Example fix

// before — backward seek can overshoot start
r.Seek(-n, io.SeekCurrent) // negative position if n > pos

// after — bound n to the current position
cur, _ := r.Seek(0, io.SeekCurrent)
if n > cur { n = cur }
r.Seek(-n, io.SeekCurrent)
Defensive patterns

Strategy: validation

Validate before calling

// Compute and validate the absolute position before Seek.
func safeSeek(r *bytes.Reader, off int64, whence int) (int64, error) {
    var abs int64
    switch whence {
    case io.SeekStart: abs = off
    case io.SeekCurrent:
        cur, _ := r.Seek(0, io.SeekCurrent); abs = cur + off
    case io.SeekEnd:
        cur, _ := r.Seek(0, io.SeekEnd); abs = cur + off
    }
    if abs < 0 {
        return 0, fmt.Errorf("seek to negative position %d", abs)
    }
    return r.Seek(off, whence)
}

Try / catch

pos, err := r.Seek(off, whence)
if err != nil && strings.Contains(err.Error(), "negative position") {
    // clamp off or bound backward seeks
}

Prevention

When it happens

Trigger: Triggered when abs < 0 after the whence computation: e.g., Seek(-5, io.SeekStart), Seek(-100, io.SeekCurrent) when fewer than 100 bytes have been read, or Seek(-N, io.SeekEnd) where N exceeds the slice length (reader.go:120-130).

Common situations: A relative backward seek (Seek(-n, io.SeekCurrent)) that overshoots the start. Computing an offset from a length that turns out larger than expected. Negative offsets passed through from Range headers or pagination cursors without clamping.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/da9c4bbb0b6d47c2. Report an issue: GitHub.