{"record":{"id":"f26224995a09e02e","repo":"plandex-ai/plandex","slug":"error-checking-if-settings-v2-json-exists-v-f26224","errorCode":null,"errorMessage":"error checking if settings-v2.json exists: %v","messagePattern":"error checking if settings-v2\\.json exists: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/cli/lib/plans.go","lineNumber":132,"sourceCode":"\n\terr := os.MkdirAll(dir, os.ModePerm)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating plan dir: %v\", err)\n\t}\n\n\tpath := filepath.Join(dir, \"settings-v2.json\")\n\n\tvar settingsByAccount *types.PlanSettingsByAccount\n\n\tbytes, err := os.ReadFile(path)\n\tif err == nil {\n\t\terr = json.Unmarshal(bytes, &settingsByAccount)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error unmarshalling settings-v2.json: %v\", err)\n\t\t}\n\t} else if !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"error checking if settings-v2.json exists: %v\", err)\n\t}\n\n\tif settingsByAccount == nil {\n\t\tsettingsByAccount = &types.PlanSettingsByAccount{}\n\t}\n\n\texistingSettings := (*settingsByAccount)[auth.Current.UserId]\n\n\tif existingSettings == nil {\n\t\texistingSettings = &types.PlanSettings{}\n\t}\n\n\texistingSettings.Branch = branch\n\t(*settingsByAccount)[auth.Current.UserId] = existingSettings\n\n\tbytes, err = json.Marshal(settingsByAccount)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshalling current plan settings: %v\", err)","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/plans.go#L114-L150","documentation":"When reading settings-v2.json, WriteCurrentBranch tolerates os.IsNotExist but treats any other read error (permission denied, path is a directory, I/O error) as fatal, wrapping it as 'error checking if settings-v2.json exists: %v'. This distinguishes 'no settings yet' from 'cannot even stat/read the file'.","triggerScenarios":"os.ReadFile on settings-v2.json fails with a non-ENOENT error: permission denied, the path exists as a directory named settings-v2.json, symlink loop, or underlying disk I/O error.","commonSituations":"settings-v2.json owned by root after a sudo run; someone created a directory named settings-v2.json; filesystem mounted read-only or failing; backup/restore tools leaving wrong permissions.","solutions":["Read the wrapped OS error; for permission issues run chmod u+rw (or chown back to your user) on the file/dir.","If settings-v2.json is a directory, remove/rename it so the CLI can write a file there.","Check filesystem health (mount flags, dmesg) if errors indicate I/O problems."],"exampleFix":"// error: ... permission denied\n$ ls -la ~/.plandex/<project>/<plan>/settings-v2.json\n$ sudo chown -R $USER ~/.plandex && chmod -R u+rw ~/.plandex\n$ plandex checkout main","handlingStrategy":"try-catch","validationCode":"path := filepath.Join(fs.HomePlandexDir, lib.CurrentProjectId, lib.CurrentPlanId, \"settings-v2.json\")\nif _, err := os.Stat(path); err != nil && !os.IsNotExist(err) {\n    return fmt.Errorf(\"cannot access settings-v2.json: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := lib.WriteCurrentBranch(branch); err != nil {\n    if strings.Contains(err.Error(), \"error checking if settings-v2.json exists\") {\n        if errors.Is(err, os.ErrPermission) {\n            return fmt.Errorf(\"fix ownership: chown -R $USER ~/.plandex\")\n        }\n        return err\n    }\n    return nil\n}","preventionTips":["Avoid sudo runs of the CLI, which change file ownership.","Ensure nothing (backup tools, mistakes) creates a directory named settings-v2.json.","Check mount flags on the home filesystem before running state-mutating commands."],"tags":["go","filesystem","permissions","cli"],"backgroundTag":"permission-denied","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}