{"record":{"id":"914b7a9393b2f50e","repo":"multica-ai/multica","slug":"create-cli-config-directory-w","errorCode":null,"errorMessage":"create CLI config directory: %w","messagePattern":"create CLI config directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/config.go","lineNumber":328,"sourceCode":"\n// SaveCLIConfig writes the CLI config to disk atomically (default profile).\nfunc SaveCLIConfig(cfg CLIConfig) error {\n\treturn SaveCLIConfigForProfile(cfg, \"\")\n}\n\n// SaveCLIConfigForProfile writes the CLI config for the given profile.\nfunc SaveCLIConfigForProfile(cfg CLIConfig, profile string) error {\n\tpath, err := CLIConfigPathForProfile(profile)\n\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}","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/config.go#L310-L346","documentation":"SaveCLIConfigForProfile could not create the profile directory tree (~/.multica/profiles/<name> or its task-local equivalent) with os.MkdirAll. The wrapped errno is usually EACCES or ENOTDIR. This is the first write-path step, before any file is produced.","triggerScenarios":"Calling SaveCLIConfig/SaveCLIConfigForProfile when the home directory or an ancestor of the target path is read-only, when a non-directory file occupies a path component (e.g. a file named 'profiles'), or when permissions deny creation.","commonSituations":"Home dir mounted read-only or full (ENOSPC surfaces similarly); earlier root-run invocation created ~/.multica as root:root 755 so the normal user cannot add subdirectories; MULTICA_TASK_CONFIG_ROOT pointing into a path where a component is a plain file.","solutions":["Check the exact path from the wrapped error; ensure every existing component is a directory and owned/writable by the current user","chown -R $(whoami) the config root if a sudo run changed ownership","Remove any plain file blocking a directory component (e.g. rm ~/.multica/profiles if it is a file)","Free disk space or remount the home directory read-write if that is the underlying errno"],"exampleFix":"# before\n$ multica config set default_workspace acme\ncreate CLI config directory: mkdir /home/user/.multica/profiles: permission denied\n\n# after\n$ sudo chown -R $(whoami) ~/.multica\n$ multica config set default_workspace acme","handlingStrategy":"validation","validationCode":"dir := filepath.Dir(configPath)\nif info, err := os.Stat(filepath.Dir(dir)); err == nil && !info.IsDir() {\n\t// a file occupies a parent component; fail early with a clear message\n}","typeGuard":null,"tryCatchPattern":"if err := cli.SaveCLIConfig(cfg); err != nil {\n\tif errors.Is(err, os.ErrPermission) || strings.Contains(err.Error(), \"create CLI config directory\") {\n\t\t// guide the user to fix ownership of the config root\n\t}\n\treturn err\n}","preventionTips":["Create the config root once at install time with correct ownership","Avoid mixing sudo and non-sudo invocations against the same home config","For task-local roots, have the daemon mkdir and chown the tree before spawning CLI subprocesses"],"tags":["filesystem","permissions","config","go"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}