{"record":{"id":"afab6ccec338589f","repo":"gastownhall/beads","slug":"s-is-not-a-directory","errorCode":null,"errorMessage":"%s is not a directory","messagePattern":"(.+?) is not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/directory_size.go","lineNumber":32,"sourceCode":"\n// MeasureDirectorySize returns the approximate size of the live file tree at\n// root. It tolerates descendants disappearing while the tree is being walked,\n// but a missing root or any other filesystem error is a failed measurement.\nfunc MeasureDirectorySize(ctx context.Context, root string) (int64, error) {\n\tif root == \"\" {\n\t\treturn 0, fmt.Errorf(\"directory path is empty\")\n\t}\n\n\tresolvedRoot, err := filepath.EvalSymlinks(root)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tinfo, err := os.Stat(resolvedRoot)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif !info.IsDir() {\n\t\treturn 0, fmt.Errorf(\"%s is not a directory\", root)\n\t}\n\n\treturn measureDirectorySizeWithWalk(ctx, resolvedRoot, filepath.Walk)\n}\n\nfunc measureDirectorySizeWithWalk(ctx context.Context, root string, walk directoryWalkFunc) (int64, error) {\n\tvar size int64\n\terr := walk(root, func(path string, info os.FileInfo, walkErr error) error {\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif walkErr != nil {\n\t\t\tif path != root && errors.Is(walkErr, fs.ErrNotExist) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn walkErr\n\t\t}\n\t\tif info == nil {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/directory_size.go#L14-L50","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nsize, err := storage.MeasureDirectorySize(ctx, cfg.DataDir) // DataDir = \"/data/beads.db\"\n// after\nif info, serr := os.Stat(cfg.DataDir); serr != nil || !info.IsDir() {\n    return fmt.Errorf(\"%s must be a directory\", cfg.DataDir)\n}\nsize, err := storage.MeasureDirectorySize(ctx, cfg.DataDir)","handlingStrategy":"validation","validationCode":"func ensureDir(path string) error {\n    resolved, err := filepath.EvalSymlinks(path)\n    if err != nil { return err }\n    info, err := os.Stat(resolved)\n    if err != nil { return err }\n    if !info.IsDir() { return fmt.Errorf(\"%s is not a directory\", path) }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := ensureDir(cfg.DataDir); err != nil {\n    return fmt.Errorf(\"invalid data dir for measurement: %w\", err)\n}\nsize, err := storage.MeasureDirectorySize(ctx, cfg.DataDir)","preventionTips":["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"],"tags":["go","validation","filesystem","directory"],"backgroundTag":"path-not-a-directory","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}