gastownhall/beads · error

failed to create dolt directory: %w

Error message

failed to create dolt directory: %w

What it means

After validating inputs and the dolt CLI, bootstrap creates the parent .beads/dolt directory with os.MkdirAll(doltDir, 0o750). This error wraps any failure from that creation — typically a permissions problem on an ancestor directory, a read-only filesystem, or a non-directory file occupying part of the path.

Source

Thrown at internal/storage/dolt/bootstrap.go:69

		return false, nil
	}

	if err := remotecache.ValidateRemoteURL(remoteURL); err != nil {
		return false, fmt.Errorf("invalid remote URL: %w", err)
	}

	if err := ValidateDatabaseName(database); err != nil {
		return false, fmt.Errorf("invalid database name %q (use cfg.GetDoltDatabase() to resolve the configured name): %w", database, err)
	}

	// Verify dolt CLI is available
	if _, err := exec.LookPath("dolt"); err != nil {
		return false, fmt.Errorf("dolt CLI not found (required for remote bootstrap): %w", err)
	}

	// Create the parent dolt directory
	if err := os.MkdirAll(doltDir, 0o750); err != nil {
		return false, fmt.Errorf("failed to create dolt directory: %w", err)
	}

	// Clone into <doltDir>/<database>/ so the embedded driver can find it.
	// `dolt clone <url> <target>` creates <target>/.dolt/ directly.
	cloneTarget := filepath.Join(doltDir, database)
	// Record whether the target already existed before this clone attempt.
	// If it did, the failed-clone cleanup below must never touch it: it
	// wasn't created by us, so it could be a pre-existing Dolt repo (e.g.
	// from an earlier bootstrap that a stale/empty doltExists() check
	// missed) that we must not delete.
	targetPreExisted := pathExists(cloneTarget)
	cmd := bootstrapCloneCmd(ctx, remoteURL, cloneTarget)
	if output, err := cmd.CombinedOutput(); err != nil {
		if targetPreExisted {
			return false, fmt.Errorf("dolt clone failed: %w\nOutput: %s\nClone target %q already existed before this attempt; left untouched to avoid deleting a pre-existing Dolt repo", err, output, cloneTarget)
		}
		cleaned, cleanupErr := removeFailedCloneTargetWithRetry(cloneTarget)
		return false, formatFailedCloneTargetError(err, output, cloneTarget, cleaned, cleanupErr)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check ownership/permissions on the .beads directory and its parents (`ls -la .beads`) and fix with chown/chmod so the running user can create subdirectories.
  2. If a file named `dolt` exists at the target path, remove or rename it (`rm .beads/dolt` if it is a stray file, after confirming it is not a symlink you need).
  3. Verify the filesystem is writable (`touch .beads/test`) — remount read-only volumes or move the workspace to a writable location.
  4. If SELinux/AppArmor denied the operation, check audit logs and adjust policy, or run in a context that permits writes to the workspace.
  5. Check disk space with `df -h .` and free space if the disk is full.

Example fix

# before
$ sudo bd doctor   # .beads/dolt created as root
$ bd bootstrap     # failed to create dolt directory: permission denied
# after
$ sudo chown -R "$USER" .beads
$ bd bootstrap
Defensive patterns

Strategy: validation

Validate before calling

if err := os.MkdirAll(doltDir, 0o750); err != nil {
    return fmt.Errorf("cannot write %s: %w", doltDir, err)
}

Try / catch

ok, err := dolt.BootstrapFromRemote(ctx, doltDir, remote)
if err != nil && strings.Contains(err.Error(), "failed to create dolt directory") {
    // check ownership/permissions of .beads and its parents before retrying
}

Prevention

When it happens

Trigger: BootstrapFromRemoteWithDB calls os.MkdirAll on the dolt directory and it fails: parent dir not writable by the current user, path component is a regular file, disk full, read-only mount, or SELinux/AppArmor denial.

Common situations: .beads owned by another user after running bd with sudo once; cloning a repo where .beads contains a committed stub file named "dolt"; running in a read-only container rootfs; NFS/locked-down corporate mounts denying creation with mode 0o750.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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