gastownhall/beads · error

multiple .doltcfg directories detected

Error message

multiple .doltcfg directories detected

What it means

ErrMultipleDoltCfgDirs is returned by resolveCfgDir when both a parent-directory .doltcfg and a data-directory .doltcfg exist. This mirrors Dolt's own ErrMultipleDoltCfgDirs: guessing which config wins risks silently losing user or branch-control settings, so the launcher treats it as a hard Start() failure instead of picking one arbitrarily.

Source

Thrown at internal/doltserver/doltserver.go:1095

// (see SupportsArchiveLevelConfig). It lives alongside the other gitignored
// server state files in beadsDir, not inside the Dolt data directory.
const doltServerConfigFileName = "dolt-server-config.yaml"

func doltServerConfigPath(beadsDir string) string {
	return filepath.Join(beadsDir, doltServerConfigFileName)
}

// doltCfgDirName mirrors commands.DefaultCfgDirName in the pinned dolt
// module (cmd/dolt/commands/sql.go) — the on-disk directory name Dolt looks
// in for privileges.db (users/passwords) and branch_control.db.
const doltCfgDirName = ".doltcfg"

// ErrMultipleDoltCfgDirs is returned by resolveCfgDir when both a parent
// and a data-directory .doltcfg exist. Mirrors Dolt's own ambiguous case
// (commands.ErrMultipleDoltCfgDirs in the pinned module) — guessing which
// one to use risks the same silent user/branch-control loss this function
// exists to prevent, so this is surfaced as a hard Start() failure instead.
var ErrMultipleDoltCfgDirs = errors.New("multiple .doltcfg directories detected")

// resolveCfgDir replicates Dolt's own flag-mode .doltcfg discovery
// (setupDoltConfig in cmd/dolt/commands/sqlserver/sqlserver.go, pinned
// module) for our generated --config YAML. setupDoltConfig returns
// immediately when --config is passed, so a deployment that previously ran
// this launcher in CLI-flag mode — where dolt auto-discovers a parent
// ../.doltcfg holding privileges.db/branch_control.db — would otherwise
// silently fall back to a fresh $data_dir/.doltcfg under --config mode:
// existing users and branch controls abandoned, new passwordless root
// (gastownhall/beads#4986).
//
// doltDir is the server's data directory (== process cwd, since neither
// --data-dir nor --doltcfg-dir are ever passed). Mirrors Dolt exactly:
//   - parent ../.doltcfg (relative to doltDir) if it exists and is a dir
//   - else doltDir/.doltcfg
//   - ErrMultipleDoltCfgDirs if BOTH exist — ambiguous, matches Dolt's own
//     ErrMultipleDoltCfgDirs case rather than guessing.
//

View on GitHub (pinned to 71377f2769)

Solutions

  1. Delete the redundant .doltcfg from either the parent or the data directory (keep the one with the intended user/branch-control settings).
  2. Merge the desired settings into a single .doltcfg and remove the other before restarting.
  3. Run the launcher with a data directory whose ancestor chain contains no other .doltcfg (e.g. clean container workdir).

Example fix

// before
$ ls /srv/beads/.doltcfg /srv/beads/data/.doltcfg  # both exist -> Start() fails
// after
$ mv /srv/beads/data/.doltcfg/user.json /srv/beads/.doltcfg/user.json  # merge, then:
$ rm -rf /srv/beads/data/.doltcfg
$ bd <start launcher>  # resolves single /srv/beads/.doltcfg
Defensive patterns

Strategy: validation

Validate before calling

// before calling Start(), detect the ambiguity yourself
func hasMultipleDoltCfgDirs(dataDir string) (bool, error) {
	abs, err := filepath.Abs(dataDir)
	if err != nil {
		return false, err
	}
	for dir := abs; ; dir = filepath.Dir(dir) {
		hits := 0
		for _, d := range []string{dir, filepath.Dir(dir)} {
			if fi, err := os.Stat(filepath.Join(d, ".doltcfg")); err == nil && fi.IsDir() {
				hits++
			}
		}
		if hits > 1 {
			return true, nil
		}
		if dir == filepath.Dir(dir) {
			break
		}
	}
	return false, nil
}

Try / catch

if err := server.Start(ctx); err != nil {
	if errors.Is(err, doltserver.ErrMultipleDoltCfgDirs) {
		// inspect parent and data dirs, keep/merge one .doltcfg, then restart
	}
	return err
}

Prevention

When it happens

Trigger: Starting the external Dolt server launcher (Start() path through resolveCfgDir) when the working/parent directory and the configured data directory each contain a .doltcfg directory. Happens when a deployment previously ran dolt in CLI-flag mode from a parent directory that auto-discovered .doltcfg, and the data dir later gained its own .doltcfg.

Common situations: Repointing the launcher's data directory after previously running dolt sql-server directly from a parent checkout that already had .doltcfg; container images where one .doltcfg is baked at / and another is created in the data volume; moving repo roots without cleaning up the old config directory.

Related errors


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