gastownhall/beads · error

create beads directory: %w

Error message

create beads directory: %w

What it means

`bd bootstrap`'s sync action ensures the `.beads` directory exists before cloning from the remote, because in the fresh-clone path remote data may be detected before `.beads` is created (GH#2792). If `os.MkdirAll` on the configured beads directory fails (permissions, read-only filesystem, path is a file), the error is wrapped as `create beads directory: ...`.

Source

Thrown at cmd/bd/bootstrap.go:722

	count, err := importFromLocalJSONL(ctx, s, plan.JSONLFile)
	if err != nil {
		return fmt.Errorf("import from JSONL: %w", err)
	}

	if err := s.Commit(ctx, "bd bootstrap: import from issues.jsonl"); err != nil {
		return fmt.Errorf("commit import: %w", err)
	}

	fmt.Fprintf(os.Stderr, "Imported %d issues from %s\n", count, plan.JSONLFile)
	return nil
}

func executeSyncAction(ctx context.Context, plan BootstrapPlan, cfg *configfile.Config) error {
	// Ensure .beads directory exists — it may not in the "fresh clone"
	// bootstrap path where we detected remote data before .beads was
	// created. Deferred here to preserve --dry-run semantics. (GH#2792)
	if err := os.MkdirAll(plan.BeadsDir, 0o750); err != nil {
		return fmt.Errorf("create beads directory: %w", err)
	}

	dbName := cfg.GetDoltDatabase()
	if err := cloneFromRemote(ctx, plan.BeadsDir, plan.SyncRemote, dbName, cfg); err != nil {
		return err
	}

	// Finalize the bootstrapped workspace so subsequent bd commands can open
	// the cloned database. Without metadata.json and config.yaml,
	// configfile.Load() returns nil, callers fall back to the default
	// dolt_database name, and bd loses track of the cloned database —
	// producing "no beads configuration found" and "Error 1105: no database
	// selected" on bd status / bd dolt push in fresh clones. Every other
	// bootstrap action (init, restore, jsonl-import) writes these files via
	// newDoltStore + createConfigYaml; the sync path historically did not.
	// (GH#3201)
	if err := finalizeSyncedBootstrap(plan.BeadsDir, plan.SyncRemote, cfg, dbName); err != nil {
		return err

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check that the repo root is writable: `ls -la .beads` — remove the blocking file if `.beads` exists as a regular file.
  2. Fix ownership/permissions: `chown`/`chmod` the repo directory or run in a writable location.
  3. If on a read-only mount (CI artifact checkout), copy the repo to a writable path first.
  4. Verify you are running `bd bootstrap` at the repository root where `.beads` belongs.

Example fix

// before (shell)
$ touch .beads   # accidentally created a file
$ bd bootstrap   // create beads directory: mkdir .beads: not a directory
// after
$ rm -f .beads
$ bd bootstrap   # succeeds
Defensive patterns

Strategy: validation

Validate before calling

# Pre-flight before bd bootstrap:
BEADS_DIR=.beads
if [ -e "$BEADS_DIR" ] && [ ! -d "$BEADS_DIR" ]; then echo "$BEADS_DIR exists and is not a directory"; exit 1; fi
[ -w . ] || { echo "repo root not writable"; exit 1; }
mount | grep -q ' ro,' && echo "filesystem mounted read-only"

Try / catch

if err := executeSyncAction(ctx, plan, cfg); err != nil {
    if strings.Contains(err.Error(), "create beads directory:") {
        return fmt.Errorf("fix .beads path/permissions and re-run bootstrap: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Running `bd bootstrap` where `plan.BeadsDir` (typically `.beads`) cannot be created: parent directory not writable, a regular file named `.beads` exists, read-only mount, or restrictive umask/ownership.

Common situations: Bootstrapping inside a read-only CI checkout; `.beads` path colliding with a file; running in a container as a non-root user without volume write permissions.

Related errors


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