{"record":{"id":"62bd3510354db33a","repo":"multica-ai/multica","slug":"read-cli-config-w","errorCode":null,"errorMessage":"read CLI config: %w","messagePattern":"read CLI config: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/config.go","lineNumber":302,"sourceCode":"}\n\n// LoadCLIConfig reads the CLI config from disk (default profile).\nfunc LoadCLIConfig() (CLIConfig, error) {\n\treturn LoadCLIConfigForProfile(\"\")\n}\n\n// LoadCLIConfigForProfile reads the CLI config for the given profile.\nfunc LoadCLIConfigForProfile(profile string) (CLIConfig, error) {\n\tpath, err := CLIConfigPathForProfile(profile)\n\tif err != nil {\n\t\treturn CLIConfig{}, err\n\t}\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\treturn CLIConfig{}, nil\n\t\t}\n\t\treturn CLIConfig{}, fmt.Errorf(\"read CLI config: %w\", err)\n\t}\n\tvar cfg CLIConfig\n\tif err := json.Unmarshal(data, &cfg); err != nil {\n\t\treturn CLIConfig{}, fmt.Errorf(\"parse CLI config: %w\", err)\n\t}\n\treturn cfg, nil\n}\n\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","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/config.go#L284-L320","documentation":"LoadCLIConfigForProfile failed to read the profile's config.json from disk with an error other than 'file does not exist' (that case returns a zero config successfully). The underlying os.ReadFile error is wrapped, so the message ends with EACCES, EISDIR, or a similar errno. It only fires when the path resolved fine but the read itself was denied.","triggerScenarios":"Calling LoadCLIConfig()/LoadCLIConfigForProfile(profile) when the config file exists but the process lacks read permission (root-owned file after running the CLI with sudo, or MULTICA_TASK_CONFIG_ROOT pointing at a directory chmod'd 0700 owned by another user), or when the path names a directory.","commonSituations":"Running the CLI once with sudo so ~/.multica/profiles/<name>/config.json becomes root-owned; a task-local root under MULTICA_TASK_CONFIG_ROOT created by a daemon running as a different user; a stray directory named config.json.","solutions":["Check ownership of the file shown in the wrapped error and chown it back to the current user (e.g. sudo chown -R $(whoami) ~/.multica)","Verify permissions with ls -l on the path; chmod 600 the file if needed","If MULTICA_TASK_CONFIG_ROOT is set, confirm it points at a directory this user owns and unset it if the daemon context is unintended","If the file is not meant to exist, delete it so the next load takes the clean 'missing file' path instead"],"exampleFix":"# before\n$ multica config get\nread CLI config: open /home/user/.multica/config.json: permission denied\n\n# after\n$ sudo chown -R $(whoami) ~/.multica\n$ multica config get","handlingStrategy":"try-catch","validationCode":"if path, err := cli.CLIConfigPathForProfile(profile); err == nil {\n\tif info, statErr := os.Stat(path); statErr == nil && info.Mode().Perm()&0o400 != 0o400 {\n\t\t// file exists but may not be readable by this user\n\t}\n}","typeGuard":"func isReadableConfigErr(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"read CLI config:\")\n}","tryCatchPattern":"cfg, err := cli.LoadCLIConfigForProfile(profile)\nif err != nil {\n\tif errors.Is(err, os.ErrPermission) {\n\t\t// surface an actionable message about ownership/permissions\n\t}\n\treturn err\n}","preventionTips":["Never run the CLI under sudo against a per-user config root","Ensure MULTICA_TASK_CONFIG_ROOT directories are owned by the user running the CLI","Check file ownership after any privileged invocation that touches ~/.multica"],"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"}