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
- Check that the task home path exists and is a directory before the prepare call
- Verify filesystem permissions along the whole path for the daemon user
- 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
- Create the task home directory before invoking prepare
- Keep workspace cleanup and task start mutually exclusive
- Run the daemon as a user with search rights on all home-path parents
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
- stat %s copy %s: %w
- mint PAT: response missing token
- daemon profile is not resolved yet; token sync skipped
- runtime local skill discovery timed out
- runtime local skill import failed
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/2fc87e3a2d5682f9.
Report an issue: GitHub.