wavetermdev/waveterm · error

error getting file: %v

Error message

error getting file: %v

What it means

GetFile loads the WaveFile for read via entry.loadFileForRead inside a read lock. A fs.ErrNotExist is passed through untouched (the normal 'file not found' case), but any other load failure is wrapped as 'error getting file: %v'. This separates genuine absence from real I/O or storage failures.

Source

Thrown at pkg/filestore/blockstore.go:184

	fileNames, err := dbGetZoneFileNames(ctx, zoneId)
	if err != nil {
		return fmt.Errorf("error getting zone files: %v", err)
	}
	for _, name := range fileNames {
		s.DeleteFile(ctx, zoneId, name)
	}
	return nil
}

// if file doesn't exsit, returns fs.ErrNotExist
func (s *FileStore) Stat(ctx context.Context, zoneId string, name string) (*WaveFile, error) {
	return withLockRtn(s, zoneId, name, func(entry *CacheEntry) (*WaveFile, error) {
		file, err := entry.loadFileForRead(ctx)
		if err != nil {
			if err == fs.ErrNotExist {
				return nil, err
			}
			return nil, fmt.Errorf("error getting file: %v", err)
		}
		return file.DeepCopy(), nil
	})
}

func (s *FileStore) ListFiles(ctx context.Context, zoneId string) ([]*WaveFile, error) {
	files, err := dbGetZoneFiles(ctx, zoneId)
	if err != nil {
		return nil, fmt.Errorf("error getting zone files: %v", err)
	}
	for idx, file := range files {
		withLock(s, file.ZoneId, file.Name, func(entry *CacheEntry) error {
			if entry.File != nil {
				files[idx] = entry.File.DeepCopy()
			}
			return nil
		})
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. First distinguish absence from failure: check errors.Is(err, fs.ErrNotExist) and handle that as 'file missing', not an error path.
  2. Inspect the wrapped cause (%v) for the real storage error; retry transient DB failures.
  3. Ensure the store and DB are healthy and open; re-open/reinitialize if closed.
  4. Check context deadlines/cancellation on the read path and use an adequate timeout.

Example fix

// before
file, err := store.GetFile(ctx, zoneId, name)
if err != nil {
	return err
}
// after
file, err := store.GetFile(ctx, zoneId, name)
if err != nil {
	if errors.Is(err, fs.ErrNotExist) {
		return createNewFile(ctx, zoneId, name)
	}
	return fmt.Errorf("get file %s/%s: %w", zoneId, name, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// distinguish absence from failure after the call; pre-check context
if ctx.Err() != nil { return ctx.Err() }

Type guard

func isNotExist(err error) bool { return errors.Is(err, fs.ErrNotExist) }

Try / catch

file, err := store.GetFile(ctx, zoneId, name)
if err != nil {
	if isNotExist(err) {
		// benign: file does not exist
		return nil
	}
	if strings.Contains(err.Error(), "error getting file:") {
		// storage failure: retry or surface
		return fmt.Errorf("get %s/%s: %w", zoneId, name, err)
	}
	return err
}

Prevention

When it happens

Trigger: entry.loadFileForRead returns a non-ErrNotExist error: the backing DB read of file metadata fails, a cache/state inconsistency occurs while materializing the file, the context is cancelled mid-load, or the stored record is corrupt.

Common situations: Underlying store closed or failing while a file is opened; context timeouts on slow storage; a file record partially deleted or corrupted so metadata loads fail; concurrent operations leaving cache entries inconsistent.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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