{"record":{"id":"283076469fa54d4b","repo":"sipeed/picoclaw","slug":"failed-to-save-config-w-283076","errorCode":null,"errorMessage":"failed to save config: %w","messagePattern":"failed to save config: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/model/add.go","lineNumber":195,"sourceCode":"\t\t\tm.Enabled = true\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found {\n\t\tcfg.ModelList = append(cfg.ModelList, &config.ModelConfig{\n\t\t\tModelName: alias,\n\t\t\tModel:     modelID,\n\t\t\tAPIBase:   apiBase,\n\t\t\tAPIKeys:   secureKeys,\n\t\t\tEnabled:   true,\n\t\t})\n\t}\n\n\tcfg.Agents.Defaults.ModelName = alias\n\n\tif err := config.SaveConfig(configPath, cfg); err != nil {\n\t\treturn fmt.Errorf(\"failed to save config: %w\", err)\n\t}\n\n\tfmt.Fprintf(stdout, \"✓ Saved model '%s' (%s) and set as default.\\n\", alias, modelID)\n\treturn nil\n}\n","sourceCodeStart":177,"sourceCodeEnd":201,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/model/add.go#L177-L201","documentation":"picoclaw model add successfully upserted the model entry in memory and set the default model name, but config.SaveConfig failed to persist it — so nothing was written. The wrapped error carries the cause: marshaling or save-time validation failure, permission denied on the file or directory, a read-only filesystem, or disk full.","triggerScenarios":"Config file or its directory owned by root after earlier sudo use while now running as a normal user; read-only mount (container, WSL); disk-full; a config state that fails picoclaw's save-time validation.","commonSituations":"Mixed sudo/non-sudo invocations leaving root-owned config files; containers with read-only home; CI runners out of disk; configs with fields that fail validation on write.","solutions":["Read the wrapped error: 'permission denied' → chown the config file and its directory to the current user; 'no space left on device' → free space","After earlier sudo runs, fix ownership: sudo chown -R $(id -u):$(id -g) <config-dir>","Re-run the command once fixed — since the save failed, there is no partial state to clean up","For validation failures, fix the field named in the error and retry"],"exampleFix":"# before\n$ picoclaw model add -b ... -k ... -m gpt-4o-mini\nfailed to save config: ... permission denied\n\n# after\n$ sudo chown -R $(id -u):$(id -g) ~/.config/picoclaw\n$ picoclaw model add -b ... -k ... -m gpt-4o-mini","handlingStrategy":"try-catch","validationCode":"configPath := internal.GetConfigPath()\nf, err := os.OpenFile(configPath, os.O_WRONLY|os.O_APPEND, 0)\nif err != nil {\n  return fmt.Errorf(\"config %s not writable: %w\", configPath, err)\n}\nf.Close()","typeGuard":"func isConfigSaveError(err error) bool {\n  return err != nil && strings.Contains(err.Error(), \"failed to save config\")\n}\n\nfunc isPermissionErr(err error) bool {\n  return errors.Is(err, os.ErrPermission) // through the %w chain\n}","tryCatchPattern":"if err := config.SaveConfig(configPath, cfg); err != nil {\n  if errors.Is(err, os.ErrPermission) {\n    // guide: chown the config dir, or rerun with corrected ownership; state was not persisted\n  }\n  if errors.Is(err, fs.ErrNoSpace) {\n    // alert on disk space; safe to retry after cleanup\n  }\n  return fmt.Errorf(\"failed to save config: %w\", err)\n}","preventionTips":["Never alternate sudo and non-sudo picoclaw runs against one config","Check writability of the config path in setup scripts","Keep disk headroom on machines that write configs frequently"],"tags":["config","cli","filesystem","persistence"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}