{"record":{"id":"72188466c8559c18","repo":"multica-ai/multica","slug":"restrict-task-local-cli-config-directory-w","errorCode":null,"errorMessage":"restrict task-local CLI config directory: %w","messagePattern":"restrict task-local CLI config directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/config.go","lineNumber":337,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\tdir := filepath.Dir(path)\n\tdirMode := os.FileMode(0o755)\n\tif strings.TrimSpace(os.Getenv(TaskConfigRootEnv)) != \"\" {\n\t\tdirMode = 0o700\n\t}\n\tif err := os.MkdirAll(dir, dirMode); err != nil {\n\t\treturn fmt.Errorf(\"create CLI config directory: %w\", err)\n\t}\n\tif dirMode == 0o700 {\n\t\troot, _, err := multicaConfigRoot()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"resolve task-local CLI config root: %w\", err)\n\t\t}\n\t\tfor current := dir; ; current = filepath.Dir(current) {\n\t\t\tif err := os.Chmod(current, 0o700); err != nil {\n\t\t\t\treturn fmt.Errorf(\"restrict task-local CLI config directory: %w\", err)\n\t\t\t}\n\t\t\tif current == root {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tparent := filepath.Dir(current)\n\t\t\tif parent == current {\n\t\t\t\treturn fmt.Errorf(\"task-local CLI config directory %q escapes root %q\", dir, root)\n\t\t\t}\n\t\t}\n\t}\n\tdata, err := json.MarshalIndent(cfg, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"encode CLI config: %w\", err)\n\t}\n\n\t// Write to a temp file in the same directory, then rename for atomicity.\n\ttmp, err := os.CreateTemp(dir, \".config-*.json.tmp\")\n\tif err != nil {","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/config.go#L319-L355","documentation":"In task-local mode (MULTICA_TASK_CONFIG_ROOT set) the save path walks from the config directory up to the task root, chmod'ing each level to 0700 so the daemon-private config is not world-readable. This error means one of those os.Chmod calls failed, typically with EPERM because the directory is owned by another user.","triggerScenarios":"Saving CLI config in task-local mode when a directory between the config dir and the root is owned by root or another user, or when running on a filesystem that does not support chmod (some NFS/FUSE/Windows mounts return EPERM).","commonSituations":"Daemon created the task root as root and then a CLI subprocess running as the service user tries to save config; MULTICA_TASK_CONFIG_ROOT pointing at a bind-mounted or network volume with restricted metadata operations.","solutions":["Align ownership: chown the entire task root to the user the CLI runs as","Run the save as the same user that owns the directory chain","Point MULTICA_TASK_CONFIG_ROOT at a plain local directory (ext4/apfs) rather than a network/FUSE mount","If 0700 restriction is not needed for your setup, unset MULTICA_TASK_CONFIG_ROOT to use the standard 0755 home layout"],"exampleFix":"# before: daemon (root) created /var/lib/multica/tasks; CLI runs as 'multica'\n$ multica config set ...\nrestrict task-local CLI config directory: chmod /var/lib/multica/tasks: operation not permitted\n\n# after\n$ sudo chown -R multica:multica /var/lib/multica/tasks","handlingStrategy":"validation","validationCode":"root := os.Getenv(\"MULTICA_TASK_CONFIG_ROOT\")\nfor p := root; ; p = filepath.Dir(p) {\n\tif info, err := os.Stat(p); err != nil || info.Mode().Perm()&0o200 == 0 {\n\t\t// ancestor not writable by this user; fix before saving\n\t}\n\tif p == \"/\" { break }\n}","typeGuard":null,"tryCatchPattern":"if err := cli.SaveCLIConfig(cfg); err != nil && strings.Contains(err.Error(), \"restrict task-local\") {\n\t// report ownership mismatch between CLI user and task-root owner\n}","preventionTips":["Run the CLI as the same user that owns the task root","Keep MULTICA_TASK_CONFIG_ROOT on a local POSIX filesystem","Set up task roots with correct ownership during provisioning, not lazily at save time"],"tags":["filesystem","permissions","environment","go"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}