{"record":{"id":"9b7bf35c8cedaaa1","repo":"plandex-ai/plandex","slug":"error-reading-settings-file-v","errorCode":null,"errorMessage":"error reading settings file: %v","messagePattern":"error reading settings file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/settings_helpers.go","lineNumber":58,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"error getting org default settings: %v\", err)\n\t\t}\n\n\t\tif defaultSettings != nil {\n\t\t\tlog.Printf(\"GetPlanSettings - found org default settings for plan %s\", plan.Id)\n\t\t\treturn defaultSettings, nil\n\t\t} else {\n\t\t\tlog.Printf(\"GetPlanSettings - no org default settings found for plan %s\", plan.Id)\n\t\t}\n\n\t\tlog.Println(\"GetPlanSettings - no default settings found, returning default settings object\")\n\t\t// if it doesn't exist, return default settings object\n\t\tsettings = &shared.PlanSettings{\n\t\t\tUpdatedAt:     plan.CreatedAt,\n\t\t\tModelPackName: shared.DefaultModelPack.Name,\n\t\t}\n\t\treturn settings, nil\n\t} else if err != nil {\n\t\treturn nil, fmt.Errorf(\"error reading settings file: %v\", err)\n\t}\n\n\tlog.Printf(\"GetPlanSettings - settings found in file\")\n\n\terr = json.Unmarshal(bytes, &settings)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error unmarshalling settings: %v\", err)\n\t}\n\n\treturn settings, nil\n}\n\nfunc StorePlanSettings(plan *Plan, settings shared.PlanSettings) error {\n\tplanDir := getPlanDir(plan.OrgId, plan.Id)\n\tsettingsPath := filepath.Join(planDir, \"settings.json\")\n\n\tsettings.UpdatedAt = time.Now()","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/settings_helpers.go#L40-L76","documentation":"GetPlanSettings reads a plan's settings file from disk; when the file exists but cannot be read (I/O error other than not-exists), the read error is wrapped with this message. It signals a filesystem-level failure, not a missing-settings condition — a missing file returns a default PlanSettings instead.","triggerScenarios":"os.ReadFile on the plan settings file returns an error other than os.IsNotExist — e.g. permission denied, path is a directory, or transient I/O failure while GetPlanSettings is called via LoadContexts, UpdateContexts, loadContexts, TellPlanHandler, or BuildPlanHandler.","commonSituations":"Settings file created by a different user/uid (permission mismatch after running app under another account), settings file corrupted by concurrent writers leaving it unreadable, or container volume mount issues.","solutions":["Check file permissions/ownership on the plan settings file and fix with chown/chmod so the server process can read it","Verify the settings file path is a regular file, not a directory or symlink to a missing target","Log the wrapped underlying error (%v of err) to identify the exact syscall failure","If on a mounted volume, confirm the mount is healthy and the file exists","Delete or restore the corrupted settings file so a fresh default is written"],"exampleFix":"// before\nreturn nil, fmt.Errorf(\"error reading settings file: %v\", err)\n// after\nreturn nil, fmt.Errorf(\"error reading settings file %s: %w\", settingsPath, err) // include path + %w for unwrapping","handlingStrategy":"fallback","validationCode":"// before calling\nif _, err := os.Stat(settingsPath); err != nil {\n    if os.IsNotExist(err) { return defaultPlanSettings(), nil } // missing file is fine\n    // permission or other I/O problem surfaces here\n}","typeGuard":null,"tryCatchPattern":"settings, err := GetPlanSettings(ctx, plan)\nif err != nil {\n    log.Printf(\"settings read failed: %v — falling back to defaults\", err)\n    settings = &shared.PlanSettings{UpdatedAt: plan.CreatedAt, ModelPackName: shared.DefaultModelPack.Name}\n}","preventionTips":["Treat not-exist as a normal case (defaults) and only surface real I/O errors","Run the server with a filesystem it owns (consistent uid across deploys)","Avoid writing settings files non-atomically; use temp file + rename","Monitor wrapped errors for permission-denied patterns"],"tags":["go","filesystem","io"],"backgroundTag":"file-read-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"}