gastownhall/beads · error
%s is not a directory
Error message
%s is not a directory
What it means
After resolving symlinks and stat-ing the root, MeasureDirectorySize verifies the target is a directory; this error is returned when the resolved path exists but is a regular file (or other non-directory). The original (unresolved) root path is included in the message.
Source
Thrown at internal/storage/directory_size.go:32
// MeasureDirectorySize returns the approximate size of the live file tree at
// root. It tolerates descendants disappearing while the tree is being walked,
// but a missing root or any other filesystem error is a failed measurement.
func MeasureDirectorySize(ctx context.Context, root string) (int64, error) {
if root == "" {
return 0, fmt.Errorf("directory path is empty")
}
resolvedRoot, err := filepath.EvalSymlinks(root)
if err != nil {
return 0, err
}
info, err := os.Stat(resolvedRoot)
if err != nil {
return 0, err
}
if !info.IsDir() {
return 0, fmt.Errorf("%s is not a directory", root)
}
return measureDirectorySizeWithWalk(ctx, resolvedRoot, filepath.Walk)
}
func measureDirectorySizeWithWalk(ctx context.Context, root string, walk directoryWalkFunc) (int64, error) {
var size int64
err := walk(root, func(path string, info os.FileInfo, walkErr error) error {
if err := ctx.Err(); err != nil {
return err
}
if walkErr != nil {
if path != root && errors.Is(walkErr, fs.ErrNotExist) {
return nil
}
return walkErr
}
if info == nil {View on GitHub (pinned to 71377f2769)
Solutions
- Point root at the data directory, not a file inside it (stat the path to confirm)
- Check symlink targets: fileResolveSymlinks then IsDir; fix or update the symlink
- Add a startup check that the configured data directory is a directory before measuring
Example fix
// before
size, err := storage.MeasureDirectorySize(ctx, cfg.DataDir) // DataDir = "/data/beads.db"
// after
if info, serr := os.Stat(cfg.DataDir); serr != nil || !info.IsDir() {
return fmt.Errorf("%s must be a directory", cfg.DataDir)
}
size, err := storage.MeasureDirectorySize(ctx, cfg.DataDir) Defensive patterns
Strategy: validation
Validate before calling
func ensureDir(path string) error {
resolved, err := filepath.EvalSymlinks(path)
if err != nil { return err }
info, err := os.Stat(resolved)
if err != nil { return err }
if !info.IsDir() { return fmt.Errorf("%s is not a directory", path) }
return nil
} Try / catch
if err := ensureDir(cfg.DataDir); err != nil {
return fmt.Errorf("invalid data dir for measurement: %w", err)
}
size, err := storage.MeasureDirectorySize(ctx, cfg.DataDir) Prevention
- Configure the data directory (not a database file) as the measurement root
- Re-check symlinks after migrations; EvalSymlinks resolves to files too
- Add a startup assertion that the configured root is a directory
When it happens
Trigger: MeasureDirectorySize(ctx, root) where root points at a file — e.g. passing a database file instead of its containing directory, or a symlink resolving to a file.
Common situations: Config pointing at the .dbell/.dolt database file rather than the data directory; a symlink that used to target a directory now targets a file after a migration; users typo-ing the path to a file.
Related errors
- directory path is empty
- no store is open for this workspace
- not found
- no absolute native user directory is available
- ExternalDoltConfig: set either Socket OR (Host, Port), not b
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/afab6ccec338589f.
Report an issue: GitHub.