AlistGo/alist · error
invalid seek: negative position
Error message
invalid seek: negative position
What it means
RangeReadReadAtSeeker.Seek computes the resulting absolute offset for SeekStart/SeekCurrent/SeekEnd and rejects negative results with 'invalid seek: negative position', returning the unchanged current offset. Seeking past the end returns io.EOF instead. The check protects underlying range readers from negative index panics.
Source
Thrown at internal/stream/stream.go:543
}
return num, err
}
func (r *RangeReadReadAtSeeker) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekStart:
case io.SeekCurrent:
if offset == 0 {
return r.masterOff, nil
}
offset += r.masterOff
case io.SeekEnd:
offset += r.ss.GetSize()
default:
return 0, errs.NotSupport
}
if offset < 0 {
return r.masterOff, errors.New("invalid seek: negative position")
}
if offset > r.ss.GetSize() {
return r.masterOff, io.EOF
}
r.masterOff = offset
return offset, nil
}
func (r *RangeReadReadAtSeeker) Read(p []byte) (n int, err error) {
if r.masterOff == 0 && r.headCache != nil {
return r.headCache.read(p)
}
rc, err := r.getReaderAtOffset(r.masterOff)
if err != nil {
return 0, err
}
n, err = rc.reader.Read(p)
rc.cur += int64(n)View on GitHub (pinned to 843d9dc814)
Solutions
- Clamp seek targets to [0, size] before calling Seek
- Fix delta computation for SeekCurrent (use absolute SeekStart when possible)
- Check the returned offset after a failed seek — it remains the pre-seek position, so state stays consistent
Example fix
// before
newPos, err := r.Seek(want-current, io.SeekCurrent)
// after: seek by absolute position
if want < 0 {
want = 0
}
newPos, err := r.Seek(want, io.SeekStart) Defensive patterns
Strategy: validation
Validate before calling
// Compute absolute target then clamp
abs := target
switch whence {
case io.SeekCurrent: abs = r.masterOff + delta
case io.SeekEnd: abs = r.ss.GetSize() + delta
}
if abs < 0 { abs = 0 } Try / catch
if _, err := r.Seek(delta, io.SeekCurrent); err != nil {
if strings.Contains(err.Error(), "negative position") {
pos, _ := r.Seek(0, io.SeekStart) // recover to a known position
}
} Prevention
- Prefer absolute SeekStart over relative SeekCurrent in rewind logic
- Clamp all computed seek targets to [0, size]
- Remember the returned offset is the pre-seek position after this error
When it happens
Trigger: Seek(-n, io.SeekStart); or Seek with a negative delta under io.SeekCurrent that crosses zero; or Seek(-n, io.SeekEnd) where n > file size.
Common situations: HTTP handler rewinding by a chunk under SeekCurrent; resumable-download code computing skip = pos - want (negative when already past the target); end-anchored parsing logic seeking backwards past position 0.
Related errors
- offset out of range
- can't create RangeReadCloser since URL is empty in link
- paths is required
- remote_path is required
- chunk_size must be positive
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/5961bcee1807dc07.
Report an issue: GitHub.