{"record":{"id":"2fc87e3a2d5682f9","repo":"multica-ai/multica","slug":"open-codex-home-s-w","errorCode":null,"errorMessage":"open codex home %s: %w","messagePattern":"open codex home (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/daemon/execenv/codex_home.go","lineNumber":968,"sourceCode":"// only the direct Codex process is terminated — descendant cleanup cannot be\n// confirmed (see server/pkg/agent/proc_windows.go) — so a leftover process that\n// knows its old CODEX_HOME can still act on it. Any local process can create the\n// same window on other platforms.\n//\n// So the identity check is bound to the handle instead of the path: compare the\n// opened directory against a no-follow stat of codexHome. A swap before the open\n// fails here (symlink, or a different directory at that path), and a swap after\n// it cannot matter, because everything downstream uses this handle rather than\n// the path.\n//\n// Scope: this covers the config-referenced copies below. The earlier steps of\n// prepareCodexHomeWithOpts still address the task home by path, so \"the whole\n// prepare is safe against a symlinked task home\" is not yet true — that\n// conversion is tracked in MUL-5647.\nfunc openVerifiedCodexHomeRoot(codexHome, key string) (*os.Root, error) {\n\troot, err := os.OpenRoot(codexHome)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"open codex home %s: %w\", codexHome, err)\n\t}\n\tif err := verifyCodexHomeRoot(root, codexHome, key); err != nil {\n\t\troot.Close()\n\t\treturn nil, err\n\t}\n\treturn root, nil\n}\n\n// verifyCodexHomeRoot proves that root is the directory codexHome names right\n// now: not reached through a symlink, and the same directory os.Lstat sees at\n// that path. It is separate from openVerifiedCodexHomeRoot so the swap case can\n// be tested deterministically instead of by racing.\nfunc verifyCodexHomeRoot(root *os.Root, codexHome, key string) error {\n\topened, err := root.Stat(\".\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"stat opened codex home %s: %w\", codexHome, err)\n\t}\n\tcurrent, err := os.Lstat(codexHome)","sourceCodeStart":950,"sourceCodeEnd":986,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/daemon/execenv/codex_home.go#L950-L986","documentation":"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.","triggerScenarios":"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.","commonSituations":"Task workspace cleanup racing task start; a moved or unmounted workspace volume; home created with a restrictive umask or wrong owner.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if fi, err := os.Stat(codexHome); err != nil || !fi.IsDir() {\n\treturn fmt.Errorf(\"task home missing or not a directory: %s\", codexHome)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["filesystem","codex-home","permissions"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}