{"record":{"id":"ae58b64e00a90183","repo":"gastownhall/beads","slug":"multiple-doltcfg-directories-detected","errorCode":null,"errorMessage":"multiple .doltcfg directories detected","messagePattern":"multiple \\.doltcfg directories detected","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/doltserver/doltserver.go","lineNumber":1095,"sourceCode":"// (see SupportsArchiveLevelConfig). It lives alongside the other gitignored\n// server state files in beadsDir, not inside the Dolt data directory.\nconst doltServerConfigFileName = \"dolt-server-config.yaml\"\n\nfunc doltServerConfigPath(beadsDir string) string {\n\treturn filepath.Join(beadsDir, doltServerConfigFileName)\n}\n\n// doltCfgDirName mirrors commands.DefaultCfgDirName in the pinned dolt\n// module (cmd/dolt/commands/sql.go) — the on-disk directory name Dolt looks\n// in for privileges.db (users/passwords) and branch_control.db.\nconst doltCfgDirName = \".doltcfg\"\n\n// ErrMultipleDoltCfgDirs is returned by resolveCfgDir when both a parent\n// and a data-directory .doltcfg exist. Mirrors Dolt's own ambiguous case\n// (commands.ErrMultipleDoltCfgDirs in the pinned module) — guessing which\n// one to use risks the same silent user/branch-control loss this function\n// exists to prevent, so this is surfaced as a hard Start() failure instead.\nvar ErrMultipleDoltCfgDirs = errors.New(\"multiple .doltcfg directories detected\")\n\n// resolveCfgDir replicates Dolt's own flag-mode .doltcfg discovery\n// (setupDoltConfig in cmd/dolt/commands/sqlserver/sqlserver.go, pinned\n// module) for our generated --config YAML. setupDoltConfig returns\n// immediately when --config is passed, so a deployment that previously ran\n// this launcher in CLI-flag mode — where dolt auto-discovers a parent\n// ../.doltcfg holding privileges.db/branch_control.db — would otherwise\n// silently fall back to a fresh $data_dir/.doltcfg under --config mode:\n// existing users and branch controls abandoned, new passwordless root\n// (gastownhall/beads#4986).\n//\n// doltDir is the server's data directory (== process cwd, since neither\n// --data-dir nor --doltcfg-dir are ever passed). Mirrors Dolt exactly:\n//   - parent ../.doltcfg (relative to doltDir) if it exists and is a dir\n//   - else doltDir/.doltcfg\n//   - ErrMultipleDoltCfgDirs if BOTH exist — ambiguous, matches Dolt's own\n//     ErrMultipleDoltCfgDirs case rather than guessing.\n//","sourceCodeStart":1077,"sourceCodeEnd":1113,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/doltserver/doltserver.go#L1077-L1113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete the redundant .doltcfg from either the parent or the data directory (keep the one with the intended user/branch-control settings).","Merge the desired settings into a single .doltcfg and remove the other before restarting.","Run the launcher with a data directory whose ancestor chain contains no other .doltcfg (e.g. clean container workdir)."],"exampleFix":"// before\n$ ls /srv/beads/.doltcfg /srv/beads/data/.doltcfg  # both exist -> Start() fails\n// after\n$ mv /srv/beads/data/.doltcfg/user.json /srv/beads/.doltcfg/user.json  # merge, then:\n$ rm -rf /srv/beads/data/.doltcfg\n$ bd <start launcher>  # resolves single /srv/beads/.doltcfg","handlingStrategy":"validation","validationCode":"// before calling Start(), detect the ambiguity yourself\nfunc hasMultipleDoltCfgDirs(dataDir string) (bool, error) {\n\tabs, err := filepath.Abs(dataDir)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tfor dir := abs; ; dir = filepath.Dir(dir) {\n\t\thits := 0\n\t\tfor _, d := range []string{dir, filepath.Dir(dir)} {\n\t\t\tif fi, err := os.Stat(filepath.Join(d, \".doltcfg\")); err == nil && fi.IsDir() {\n\t\t\t\thits++\n\t\t\t}\n\t\t}\n\t\tif hits > 1 {\n\t\t\treturn true, nil\n\t\t}\n\t\tif dir == filepath.Dir(dir) {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn false, nil\n}","typeGuard":null,"tryCatchPattern":"if err := server.Start(ctx); err != nil {\n\tif errors.Is(err, doltserver.ErrMultipleDoltCfgDirs) {\n\t\t// inspect parent and data dirs, keep/merge one .doltcfg, then restart\n\t}\n\treturn err\n}","preventionTips":["Keep exactly one .doltcfg in the deployment: bake it into the data volume, not image ancestors like /.","After migrating from dolt CLI-flag mode, audit the parent directory for a leftover .doltcfg.","In containers, set the working directory to the data dir so dolt's auto-discovery cannot pick up an ancestor config."],"tags":["dolt","configuration","startup","filesystem"],"backgroundTag":"ambiguous-config-directory","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}