#3561: fix: fail fast when both state dirs exist
cli
commands
Cluster:
Cross-Platform Fixes
Fixes #3533
## Problem
When both `~/.clawdbot` (legacy) and `~/.moltbot` (new) state directories exist, Moltbot enters a split-brain state:
- Pairing appears to succeed (`paired.json` contains the device)
- But the gateway returns: `disconnected (1008): pairing required` or `unauthorized`
- The warning "State dir migration skipped: target already exists" was printed, but Moltbot continued running
This happens because different parts of the system read/write different directories, causing credentials and pairing data to be stored in one location but read from another.
## Solution
Moltbot now refuses to start if both directories exist, displaying a clear error message with resolution options:
State directory conflict: both legacy and new directories exist.
Legacy: ~/.clawdbot
New: ~/.moltbot
To fix, choose ONE of these options:
Keep the legacy directory (recommended if you have existing data):
Remove or rename ~/.moltbot
Use the new directory:
Remove or rename ~/.clawdbot
Merge manually, then remove the legacy directory
Set an explicit state directory:
export MOLTBOT_STATE_DIR="~/.moltbot"
## Changes
- Add `StateDirConflictError` class for explicit error handling
- Add `detectStateDirConflict()` for early detection at CLI startup
- Throw error instead of warning in `autoMigrateLegacyStateDir()`
- Add comprehensive tests for conflict detection
## Testing
- Added 6 new tests for `detectStateDirConflict()`
- Updated existing test to expect `StateDirConflictError` instead of warning
- All unit tests pass
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR aims to prevent a split-brain state when both legacy and newer state directories exist by introducing a dedicated `StateDirConflictError`, improving the user-facing remediation message, and updating state-dir migration behavior to throw instead of only warning. It also adds unit tests around conflict detection.
Main integration points are the state migration layer (`src/infra/state-migrations.ts`) and path resolution utilities (`src/config/paths.ts`). The intended UX is to fail fast at CLI startup when both dirs exist, rather than continuing with inconsistent reads/writes.
<h3>Confidence Score: 3/5</h3>
- Moderately safe to merge, but key wiring issues may prevent the intended fail-fast behavior.
- The core idea (throwing on conflict) is sound and tests were added, but the CLI entrypoint currently only imports the new functions without invoking them, and the conflict detector/message use different env var names than the rest of the codebase. Those issues could lead to either no fail-fast at all or confusing remediation instructions.
- src/cli/run-main.ts, src/config/paths.ts, src/infra/state-migrations.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#11602: fix(config): skip stale legacy config files when openclaw.json exists
by akoscz · 2026-02-08
81.2%
#17916: [ fix ] : correct config directory path during onboarding
by Dijo-404 · 2026-02-16
80.4%
#4238: Fix/docker migration atomicity
by ricardotrevisan · 2026-01-29
79.7%
#5638: fix: rewrite sessionFile paths during state dir migration
by lailoo · 2026-01-31
79.3%
#3207: fix: use .moltbot for device identity directory
by aadeina · 2026-01-28
79.2%
#3783: Fix/3038 cli name moltbot
by Shuimo03 · 2026-01-29
79.1%
#3513: fix(docker): add MOLTBOT_STATE_DIR to resolve permission error
by Suksham-sharma · 2026-01-28
78.4%
#3965: Fix Docker Compose configuration for moltbot deployment
by YoByron · 2026-01-29
76.4%
#12368: fix(cron): respect OPENCLAW_STATE_DIR for cron store path
by ComBba · 2026-02-09
76.2%
#4467: fix: DEFAULT_SANDBOX_WORKSPACE_ROOT respects STATE_DIR
by Aphroq · 2026-01-30
76.1%