{"record":{"id":"e19f5dba082a8fdd","repo":"Billionmail/BillionMail","slug":"option-not-found","errorCode":null,"errorMessage":"option not found","messagePattern":"option not found","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/internal/service/public/options_mgr.go","lineNumber":90,"sourceCode":"\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\n\t\tif err != nil {\n\t\t\treturn errors.New(\"failed to get option from database: \" + err.Error())\n\t\t}\n\n\t\tif result.Value == \"\" {\n\t\t\treturn errors.New(\"option not found\")\n\t\t}\n\n\t\tjsonValue = result.Value\n\n\t\t// Store in cache\n\t\tif err := o.cache.Set(ctx, cacheKey, jsonValue, o.expiration); err != nil {\n\t\t\tg.Log().Warning(ctx, \"Failed to set option cache:\", err)\n\t\t}\n\t} else {\n\t\t// Cache hit\n\t\tjsonValue = cached.String()\n\t}\n\n\t// Deserialize\n\treturn o.deserialize(jsonValue, ptr)\n}\n\n// GetAllOptions Get all options","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/public/options_mgr.go#L72-L108","documentation":"After a cache miss, GetOption looks up the option row in bm_options. If the query succeeds but the value column is empty (row absent or stored value is empty string), it returns 'option not found'. This is the library's way of signaling that the requested option key has never been set.","triggerScenarios":"GetOption with a key that was never written via SetOption, or an option whose stored value was cleared to empty string.","commonSituations":"Reading optional settings on a fresh install before an admin saved them; typos in option key names; options removed by a cleanup/migration script.","solutions":["Verify the exact key name exists: SELECT value FROM bm_options WHERE name = '<key>'.","Call SetOption to create the option before reading it, or seed defaults during setup.","Treat the error as 'use default': call sites should fall back to a sensible default value.","Check for key-name typos or renames between code versions."],"exampleFix":"// before\nif err := public.GetOption(ctx, \"warmup_enabled\", &enabled); err != nil {\n    return err\n}\n// after\nif err := public.GetOption(ctx, \"warmup_enabled\", &enabled); err != nil {\n    if err.Error() == \"option not found\" {\n        enabled = true // default\n        return nil\n    }\n    return err\n}","handlingStrategy":"fallback","validationCode":"// check existence first if you must\nn, _ := g.DB().Model(\"bm_options\").Where(\"name\", key).Count()\nexists := n > 0","typeGuard":null,"tryCatchPattern":"if err := public.GetOption(ctx, key, &out); err != nil {\n    if err.Error() == \"option not found\" {\n        return defaultOut, nil\n    }\n    return err\n}","preventionTips":["Provide a typed wrapper that returns (value, found, err) semantics instead of string-matching.","Seed all expected options with defaults during installation/migration.","Centralize option key constants to avoid typos.","On read, write back the default via SetOption so subsequent reads succeed."],"tags":["options","missing-data","defaults"],"backgroundTag":"option-not-found","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}