gastownhall/beads · error

checking dolt compatibility marker %s: %w

Error message

checking dolt compatibility marker %s: %w

What it means

MarkDoltDirCompatible stats the compatibility marker <doltDir>/.bd-dolt-ok after confirming .dolt exists (internal/doltserver/doltserver.go:1918). If the marker is absent it is written; if os.Stat fails for a reason other than NotExist (permission denied, EIO, etc.) this error is returned. It is NOT the 'pre-0.56 incompatible database' case — that case returns a separate incompatibility error, not this one. The wrapped *PathError pinpoints why the marker path could not be examined.

Source

Thrown at internal/doltserver/doltserver.go:1918

// directory, which lets server and repair paths call it defensively.
func MarkDoltDirCompatible(doltDir string) error {
	if doltDir == "" {
		return errors.New("dolt directory is required")
	}
	dotDolt := filepath.Join(doltDir, ".dolt")
	if info, err := os.Stat(dotDolt); err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("checking dolt metadata directory %s: %w", dotDolt, err)
	} else if !info.IsDir() {
		return fmt.Errorf("dolt metadata path %s is not a directory", dotDolt)
	}
	markerPath := filepath.Join(doltDir, bdDoltMarker)
	if _, err := os.Stat(markerPath); err == nil {
		return nil
	} else if !os.IsNotExist(err) {
		return fmt.Errorf("checking dolt compatibility marker %s: %w", markerPath, err)
	}
	if err := os.WriteFile(markerPath, []byte("ok\n"), 0600); err != nil {
		return fmt.Errorf("writing dolt compatibility marker %s: %w", markerPath, err)
	}
	return nil
}

// ensureDoltInit initializes a dolt database directory if .dolt/ doesn't exist.
// If .dolt/ exists, seeds the .bd-dolt-ok marker for existing working databases.
// See GH#2137 for background on pre-0.56 database compatibility.
func ensureDoltInit(doltDir string) error {
	if err := os.MkdirAll(doltDir, config.BeadsDirPerm); err != nil {
		return fmt.Errorf("creating dolt directory: %w", err)
	}

	dotDolt := filepath.Join(doltDir, ".dolt")

	if _, err := os.Stat(dotDolt); err == nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped os.PathError — the errno (permission denied vs I/O error) tells you which branch to take.
  2. Fix ownership/permissions: `ls -ld <doltDir>` and `chown -R $USER <doltDir>` or `chmod u+rwx <doltDir>` if a prior sudo run claimed it.
  3. Check for immutable flags: `lsattr <doltDir>/.bd-dolt-ok` and `chattr -i` if set.
  4. If dmesg/filesystem logs show I/O errors, back up the data and check disk health (`smartctl`) before proceeding.
  5. Check audit logs for SELinux/AppArmor denials and adjust policy or move the data dir to an allowed path.

Example fix

// before: earlier sudo run left the dir root-owned
$ ls -ld ~/.beads/dolt
drwx------ 2 root root ... .beads/dolt
// after
$ sudo chown -R $USER:$USER ~/.beads/dolt
$ bd ready   # marker check succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Verify the marker path is examinable before running bd
import ("os"; "path/filepath")
func markerAccessible(doltDir string) error {
    if _, err := os.Stat(filepath.Join(doltDir, ".bd-dolt-ok")); err != nil {
        if os.IsNotExist(err) { return nil } // absent is fine; bd will create it
        return err
    }
    return nil
}

Try / catch

// Go: unwrap the PathError to classify the failure
if err := MarkDoltDirCompatible(dir); err != nil && strings.HasPrefix(err.Error(), "checking dolt compatibility marker") {
    var perr *os.PathError
    if errors.As(err, &perr) {
        switch {
        case errors.Is(perr.Err, syscall.EACCES):
            return fmt.Errorf("chmod/chown %s for the current user", dir)
        case errors.Is(perr.Err, syscall.EIO):
            return fmt.Errorf("disk I/O error on %s — check storage health", dir)
        }
    }
    return err
}

Prevention

When it happens

Trigger: os.Stat on <doltDir>/.bd-dolt-ok fails with something other than ENOENT while MarkDoltDirCompatible runs from bd server/repair paths: no write/search permission on doltDir, immutable file attribute, failing disk (EIO), or a security policy (e.g. sandbox denying stat on dotfiles).

Common situations: dolt dir owned by another user after running bd with sudo once; chattr +i applied to the marker by an overzealous hardening script; failing SSD returning I/O errors; AppArmor/SELinux denials on the bd binary accessing the profile directory.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d3b6df675d3ebff7. Report an issue: GitHub.