plandex-ai/plandex · error

error creating plan dir: %v

Error message

error creating plan dir: %v

What it means

After validating globals, WriteCurrentBranch ensures the plan directory ~/.plandex/<projectId>/<planId> exists with os.MkdirAll(dir, os.ModePerm). If that filesystem call fails — permissions, disk full, path is a file, etc. — the error is wrapped as 'error creating plan dir: %v'. The inner OS error is preserved in the message.

Source

Thrown at app/cli/lib/plans.go:118

func WriteCurrentBranch(branch string) error {
	if fs.HomePlandexDir == "" {
		return fmt.Errorf("HomePlandexDir not set")
	}

	if CurrentProjectId == "" || HomeCurrentPlanPath == "" {
		return fmt.Errorf("no current project")
	}

	if CurrentPlanId == "" {
		return fmt.Errorf("no current plan")
	}

	dir := filepath.Join(fs.HomePlandexDir, CurrentProjectId, CurrentPlanId)

	err := os.MkdirAll(dir, os.ModePerm)

	if err != nil {
		return fmt.Errorf("error creating plan dir: %v", err)
	}

	path := filepath.Join(dir, "settings-v2.json")

	var settingsByAccount *types.PlanSettingsByAccount

	bytes, err := os.ReadFile(path)
	if err == nil {
		err = json.Unmarshal(bytes, &settingsByAccount)
		if err != nil {
			return fmt.Errorf("error unmarshalling settings-v2.json: %v", err)
		}
	} else if !os.IsNotExist(err) {
		return fmt.Errorf("error checking if settings-v2.json exists: %v", err)
	}

	if settingsByAccount == nil {
		settingsByAccount = &types.PlanSettingsByAccount{}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the wrapped OS error in the message: fix the specific cause (permissions with chmod/chown, delete the blocking file, free disk space).
  2. Verify ~/.plandex is a writable directory owned by the current user; avoid running the CLI with sudo.
  3. If ids look wrong, repair project/plan state (current-plans-v2.json) so CurrentProjectId/CurrentPlanId are valid id strings.

Example fix

// diagnose
$ plandex checkout main
// error creating plan dir: mkdir ~/.plandex/p1/pl2: not a directory
$ ls -la ~/.plandex/p1
$ rm ~/.plandex/p1/pl2   # if it's a stray file, then retry
$ plandex checkout main
Defensive patterns

Strategy: try-catch

Validate before calling

dir := filepath.Join(fs.HomePlandexDir, lib.CurrentProjectId, lib.CurrentPlanId)
if st, err := os.Stat(dir); err == nil && !st.IsDir() {
    return fmt.Errorf("%s exists and is not a directory", dir)
}

Try / catch

if err := lib.WriteCurrentBranch(branch); err != nil {
    var pe *os.PathError
    if errors.As(err, &pe) && strings.Contains(err.Error(), "error creating plan dir") {
        log.Fatalf("plan dir unavailable at %s: %v — check permissions/disk", pe.Path, pe.Err)
    }
    return err
}

Prevention

When it happens

Trigger: os.MkdirAll fails for ~/.plandex/<projectId>/<planId>: parent path exists as a regular file, read-only filesystem, permission denied, disk full, or an invalid path component (e.g. CurrentProjectId containing path separators or reserved names).

Common situations: A stray file named like a plan id blocking directory creation; ~/.plandex owned by another user after running with sudo; read-only home dir or full disk; corrupted project/plan ids containing '/' breaking the joined path.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/f197be7ba535c754. Report an issue: GitHub.