multica-ai/multica · error

open codex home %s: %w

Error message

open codex home %s: %w

What it means

Returned by openVerifiedCodexHomeRoot when os.OpenRoot fails on the per-task CODEX_HOME directory. The daemon opens the task home as an os.Root so all later writes are confined to it; if the directory does not exist, is not a directory, or permissions deny it, this error wraps the underlying syscall failure.

Source

Thrown at server/internal/daemon/execenv/codex_home.go:968

// only the direct Codex process is terminated — descendant cleanup cannot be
// confirmed (see server/pkg/agent/proc_windows.go) — so a leftover process that
// knows its old CODEX_HOME can still act on it. Any local process can create the
// same window on other platforms.
//
// So the identity check is bound to the handle instead of the path: compare the
// opened directory against a no-follow stat of codexHome. A swap before the open
// fails here (symlink, or a different directory at that path), and a swap after
// it cannot matter, because everything downstream uses this handle rather than
// the path.
//
// Scope: this covers the config-referenced copies below. The earlier steps of
// prepareCodexHomeWithOpts still address the task home by path, so "the whole
// prepare is safe against a symlinked task home" is not yet true — that
// conversion is tracked in MUL-5647.
func openVerifiedCodexHomeRoot(codexHome, key string) (*os.Root, error) {
	root, err := os.OpenRoot(codexHome)
	if err != nil {
		return nil, fmt.Errorf("open codex home %s: %w", codexHome, err)
	}
	if err := verifyCodexHomeRoot(root, codexHome, key); err != nil {
		root.Close()
		return nil, err
	}
	return root, nil
}

// verifyCodexHomeRoot proves that root is the directory codexHome names right
// now: not reached through a symlink, and the same directory os.Lstat sees at
// that path. It is separate from openVerifiedCodexHomeRoot so the swap case can
// be tested deterministically instead of by racing.
func verifyCodexHomeRoot(root *os.Root, codexHome, key string) error {
	opened, err := root.Stat(".")
	if err != nil {
		return fmt.Errorf("stat opened codex home %s: %w", codexHome, err)
	}
	current, err := os.Lstat(codexHome)

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Check that the task home path exists and is a directory before the prepare call
  2. Verify filesystem permissions along the whole path for the daemon user
  3. Recreate the task workspace if it was deleted out from under a queued task
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(codexHome); err != nil || !fi.IsDir() {
	return fmt.Errorf("task home missing or not a directory: %s", codexHome)
}

Prevention

When it happens

Trigger: prepareCodexHome runs before the task home directory exists; the task home was deleted or renamed by another process between creation and open; the daemon user lacks execute/search permission on a parent of the home path.

Common situations: Task workspace cleanup racing task start; a moved or unmounted workspace volume; home created with a restrictive umask or wrong owner.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/2fc87e3a2d5682f9. Report an issue: GitHub.