multica-ai/multica · error

stat codex home %s: %w

Error message

stat codex home %s: %w

What it means

verifyCodexHomeRoot stats the codexHome path with os.Lstat to compare against the opened handle. Failure means the path cannot be stat'ed at all: it vanished between os.OpenRoot and the Lstat, or a parent directory became unreadable.

Source

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

	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)
	if err != nil {
		return fmt.Errorf("stat codex home %s: %w", codexHome, err)
	}
	if current.Mode()&os.ModeSymlink != 0 {
		return fmt.Errorf("codex home %s is a symlink; refusing to write %s through it", codexHome, key)
	}
	if !os.SameFile(opened, current) {
		return fmt.Errorf("codex home %s was replaced while opening it; refusing to write %s through it", codexHome, key)
	}
	return nil
}

// materialiseInCodexHome writes src to relPath inside codexHome using
// root-scoped operations, so no symlink below the task home can redirect the
// daemon's mkdir, remove, or write outside it.
//
// This matters because a task home is reused: a prepare can run against a
// directory a previous task already wrote to. Without the root, a task that
// replaced an intermediate directory of its own home with a link to somewhere
// else would have the daemon delete and overwrite the link target on the next

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Serialize workspace cleanup with task start (don't delete a home while a prepare is in flight)
  2. Retry prepare after the race window passes
  3. Ensure parent directories of the home stay searchable by the daemon user
Defensive patterns

Strategy: retry

Try / catch

if err := prepareCodexHome(...); err != nil {
	if errors.Is(err, fs.ErrNotExist) && strings.Contains(err.Error(), "stat codex home") {
		// home vanished mid-open: recreate and retry once
		_ = os.MkdirAll(codexHome, 0o755)
		err = prepareCodexHome(...)
	}
}

Prevention

When it happens

Trigger: The task home directory is removed concurrently right after being opened; a parent of the path is chmod'ed to deny search; the mount disappears.

Common situations: Workspace reaper or cleanup job racing task start; a task deleting its own home in a previous run's teardown.

Related errors


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