{"record":{"id":"643d5c74572c1b99","repo":"sipeed/picoclaw","slug":"config-is-nil","errorCode":null,"errorMessage":"config is nil","messagePattern":"config is nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/mcp/helpers.go","lineNumber":108,"sourceCode":"      \"required\": [\"mcp\"],\n      \"additionalProperties\": true\n    }\n  },\n  \"required\": [\"tools\"],\n  \"additionalProperties\": true\n}`\n\nfunc loadConfig() (*config.Config, error) {\n\tcfg, err := config.LoadConfig(internal.GetConfigPath())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load config: %w\", err)\n\t}\n\treturn cfg, nil\n}\n\nfunc saveValidatedConfig(cfg *config.Config) error {\n\tif cfg == nil {\n\t\treturn fmt.Errorf(\"config is nil\")\n\t}\n\n\tnormalizedCfg := normalizedConfigForSave(cfg)\n\n\tdata, err := json.Marshal(normalizedCfg)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to serialize config: %w\", err)\n\t}\n\n\tif err := validateConfigDocument(data); err != nil {\n\t\treturn err\n\t}\n\n\tif err := config.SaveConfig(internal.GetConfigPath(), normalizedCfg); err != nil {\n\t\treturn fmt.Errorf(\"failed to save config: %w\", err)\n\t}\n\n\treturn nil","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/mcp/helpers.go#L90-L126","documentation":"saveValidatedConfig refuses a nil *config.Config (helpers.go:107-109). It is a defensive invariant guard: the CLI always passes a config obtained from loadConfig, so a nil reaching this point means a caller ignored a load error or a new code path forgot the nil check. Not triggerable by end-user input.","triggerScenarios":"Calling the save path with a nil pointer after swallowing a loadConfig error; a new subcommand wired up without propagating the load failure.","commonSituations":"Contributors adding mcp subcommands; unit tests stubbing config loading with nil.","solutions":["Propagate the loadConfig error and return early instead of continuing with nil","nil-check cfg immediately after loading, before any mutation or save"],"exampleFix":"// before\ncfg, err := loadConfig()\nif err != nil {\n\tlog.Println(err) // swallowed\n}\n_ = saveValidatedConfig(cfg) // cfg may be nil\n\n// after\ncfg, err := loadConfig()\nif err != nil {\n\treturn err\n}\nreturn saveValidatedConfig(cfg)","handlingStrategy":"validation","validationCode":"cfg, err := loadConfig()\nif err != nil {\n\treturn err // never continue with a possibly-nil cfg\n}\nif cfg == nil {\n\treturn fmt.Errorf(\"config load returned nil without error\")\n}\nreturn saveValidatedConfig(cfg)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always check the load error before using the config pointer","Never swallow loadConfig errors to 'continue anyway'"],"tags":["go","defensive","config","internal"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}