{"record":{"id":"aa1a724ddb5219b5","repo":"Billionmail/BillionMail","slug":"pointer-cannot-be-nil","errorCode":null,"errorMessage":"pointer cannot be nil","messagePattern":"pointer cannot be nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/public/options_mgr.go","lineNumber":66,"sourceCode":"\t}\n\n\t// Update cache\n\tcacheKey := o.buildCacheKey(key)\n\tif err := o.cache.Set(ctx, cacheKey, jsonValue, o.expiration); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// GetOption Get option and deserialize to specified type\nfunc (o *OptionsMgr) GetOption(ctx context.Context, key string, ptr interface{}) error {\n\tif key == \"\" {\n\t\treturn errors.New(\"key cannot be empty\")\n\t}\n\n\tif ptr == nil {\n\t\treturn errors.New(\"pointer cannot be nil\")\n\t}\n\n\t// Try to get from cache\n\tcacheKey := o.buildCacheKey(key)\n\tcached, err := o.cache.Get(ctx, cacheKey)\n\tvar jsonValue string\n\n\tif err != nil || cached == nil {\n\t\t// Cache miss, read from database\n\t\tvar result struct {\n\t\t\tValue string `json:\"value\"`\n\t\t}\n\n\t\terr := g.DB().Model(\"bm_options\").\n\t\t\tWhere(\"name\", key).\n\t\t\tFields(\"value\").\n\t\t\tScan(&result)\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/public/options_mgr.go#L48-L84","documentation":"GetOption deserializes the stored value into the caller-provided pointer (ptr interface{}). If ptr is nil there is nowhere to put the result, so the call is rejected up front. The value must be a non-nil pointer to the destination type (e.g. *string, *int, *struct).","triggerScenarios":"Calling GetOption(ctx, \"some_key\", nil), or passing a nil typed pointer such as var s *string; GetOption(ctx, \"k\", s).","commonSituations":"Dynamic calls where the destination variable is conditionally allocated; reflection-based generic loaders that build the destination lazily and skip allocation.","solutions":["Pass the address of a declared variable: GetOption(ctx, \"k\", &out).","Initialize typed pointers before the call (out = new(string)).","Guard in generic code: if ptr == nil || reflect.ValueOf(ptr).Kind() != reflect.Ptr { return error }."],"exampleFix":"// before\nvar cfg *SmtpConfig\npublic.GetOption(ctx, \"smtp\", cfg) // nil pointer\n// after\ncfg := &SmtpConfig{}\npublic.GetOption(ctx, \"smtp\", cfg)","handlingStrategy":"validation","validationCode":"var out SmtpConfig\nif out == (SmtpConfig{}) { /* still fine; only nil pointer is bad */ }\n// ensure you pass &out, not out or nil\nerr := public.GetOption(ctx, \"smtp\", &out)","typeGuard":"func validDest(ptr any) bool {\n    if ptr == nil {\n        return false\n    }\n    return reflect.ValueOf(ptr).Kind() == reflect.Ptr && !reflect.ValueOf(ptr).IsNil()\n}","tryCatchPattern":"if err := public.GetOption(ctx, key, dest); err != nil {\n    if strings.Contains(err.Error(), \"pointer cannot be nil\") {\n        return ErrBadDestination\n    }\n    return err\n}","preventionTips":["Always pass the address-of a concrete variable to GetOption.","Wrap the getter in a typed helper (GetSMTPConfig(ctx)) so callers can't pass wrong destinations.","Keep generic reflection-based loaders responsible for allocating the destination before delegating."],"tags":["validation","options","nil-pointer"],"backgroundTag":"nil-argument","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}