wavetermdev/waveterm · error
offset is past the end of the file
Error message
offset is past the end of the file
What it means
WriteAt only allows writing within or exactly at the end of the existing file. If offset is strictly greater than the file's current Size, the write would create a hole, which the block/part model does not support, so it is rejected after loading the file into cache.
Source
Thrown at pkg/filestore/blockstore.go:251
}
entry.writeAt(0, data, true)
// since WriteFile can *truncate* the file, we need to flush the file to the DB immediately
return entry.flushToDB(ctx, true)
})
}
func (s *FileStore) WriteAt(ctx context.Context, zoneId string, name string, offset int64, data []byte) error {
if offset < 0 {
return fmt.Errorf("offset must be non-negative")
}
return withLock(s, zoneId, name, func(entry *CacheEntry) error {
err := entry.loadFileIntoCache(ctx)
if err != nil {
return err
}
file := entry.File
if offset > file.Size {
return fmt.Errorf("offset is past the end of the file")
}
partMap := file.computePartMap(offset, int64(len(data)))
incompleteParts := incompletePartsFromMap(partMap)
err = entry.loadDataPartsIntoCache(ctx, incompleteParts)
if err != nil {
return err
}
entry.writeAt(offset, data, false)
return nil
})
}
func (s *FileStore) AppendData(ctx context.Context, zoneId string, name string, data []byte) error {
return withLock(s, zoneId, name, func(entry *CacheEntry) error {
err := entry.loadFileIntoCache(ctx)
if err != nil {
return err
}View on GitHub (pinned to a4447c1563)
Solutions
- Read the current file size first (ReadAt with size 0 or a stat call) and clamp offset to <= size.
- To extend the file, write sequentially from the current end rather than seeking past it.
- Refresh any cached size before each WriteAt when multiple writers exist.
Example fix
// before offset := lastKnownSize + 1024 fs.WriteAt(ctx, zone, name, offset, data) // may exceed current size // after offset := lastKnownSize fs.WriteAt(ctx, zone, name, offset, data) // write at/within EOF
Defensive patterns
Strategy: validation
Validate before calling
size := fileSize(zone, name) // from stat or a 0-size ReadAt
if offset > size { return fmt.Errorf("offset %d beyond EOF %d", offset, size) } Type guard
func inBounds(offset, fileSize int64) bool { return offset >= 0 && offset <= fileSize } Prevention
- Always refresh the file size before writing; never trust stale cached sizes with concurrent writers.
- Extend files sequentially from EOF instead of seeking past it.
- Detect the specific message 'offset is past the end of the file' and re-stat then retry.
When it happens
Trigger: Calling WriteAt with an offset larger than entry.File.Size — e.g. writing past EOF, using a stale cached size after another writer truncated/rewrote the file, or seeking-based logic that assumes sparse writes are allowed.
Common situations: Concurrent writers where one process shrinks the file while another uses a previously computed offset; resuming a write after the file was recreated smaller; porting code that assumed sparse/seek-past-EOF writes (like os.File.WriteAt on a sparse file).
Related errors
- offset cannot be negative
- error appending to blockfile: %w
- error truncating blockfile: %w
- error creating block term file: %w
- error creating blockfile: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/58f6b204eb18b33c.
Report an issue: GitHub.