{"record":{"id":"83f589ccd1a1e3f7","repo":"JuliusBrussee/caveman","slug":"cacheengine-nil-engine","errorCode":null,"errorMessage":"cacheengine: nil engine","messagePattern":"cacheengine: nil engine","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/engine.go","lineNumber":105,"sourceCode":"\t\tif provider == \"\" || driver == nil {\n\t\t\treturn nil, errors.New(\"cacheengine: driver needs non-empty provider and implementation\")\n\t\t}\n\t\tif _, exists := drivers[provider]; exists {\n\t\t\treturn nil, fmt.Errorf(\"cacheengine: duplicate normalized driver provider %q\", provider)\n\t\t}\n\t\tdrivers[provider] = driver\n\t}\n\treturn &Engine{\n\t\tguard: cacheguard.New(), prefixSafety: newPrefixSafetyCache(8192),\n\t\tmaxKeyShards: maxShards, maxRequestBytes: maxRequestBytes, maxStablePrefixBytes: maxStablePrefixBytes,\n\t\tresolveProfile: resolver, drivers: drivers,\n\t}, nil\n}\n\n// Plan selects profitable stable-prefix cache boundaries without editing wire bytes.\nfunc (e *Engine) Plan(request PlanRequest) (Plan, error) {\n\tif e == nil || e.guard == nil || e.prefixSafety == nil {\n\t\treturn Plan{}, errors.New(\"cacheengine: nil engine\")\n\t}\n\tif e.configErr != nil {\n\t\treturn Plan{}, e.configErr\n\t}\n\tif err := validatePlanRequest(request); err != nil {\n\t\treturn Plan{}, err\n\t}\n\tprofile := normalizedProfile(request.Profile)\n\tplan := Plan{\n\t\tDecision:       DecisionPassThrough,\n\t\tReason:         ReasonUnsupported,\n\t\tProfileID:      profile.ID,\n\t\tMode:           profile.Mode,\n\t\tAttribution:    profile.Attribution,\n\t\tEconomicsBasis: \"modeled_input_rate_units\",\n\t\tKeyShardCount:  1,\n\t}\n\tif !profile.EconomicsKnown {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/engine.go#L87-L123","documentation":"Returned by (*Engine).Plan when the receiver is nil or incompletely initialized (nil guard or nil prefixSafety cache). Because Engine methods are value-safe on nil receivers in this codebase's style, this error signals the caller constructed Engine{} directly or lost the NewEngine result (including the NewEngine-error path) instead of obtaining a fully initialized engine.","triggerScenarios":"Calling Plan on var e *Engine after NewEngine returned an error and the nil engine was used anyway; copying Engine{} as a zero-value struct; storing the engine in a field that a failed initializer left nil.","commonSituations":"Ignoring the error from cacheengine.NewEngine and proceeding with the nil result; dependency-injection wiring that leaves the engine field nil when construction fails; races where the engine is used before initialization completes.","solutions":["Always construct via engine, err := cacheengine.NewEngine(cfg) and return/abort on err before using engine","Audit for zero-value Engine{} literals — they never pass the guard/prefixSafety nil checks","If DI wiring leaves it nil, fail construction of the parent component too"],"exampleFix":"// before\ne, _ := cacheengine.NewEngine(cfg)\nplan, err := e.Plan(req) // e is nil when cfg was invalid\n\n// after\ne, err := cacheengine.NewEngine(cfg)\nif err != nil {\n    return err\n}\nplan, err := e.Plan(req)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func engineReady(e *cacheengine.Engine) bool {\n\treturn e != nil\n}","tryCatchPattern":"plan, err := eng.Plan(req)\nif err != nil {\n\tif err.Error() == \"cacheengine: nil engine\" {\n\t\treturn plan, errors.New(\"engine not initialized; NewEngine failed or was skipped\")\n\t}\n\treturn plan, err\n}","preventionTips":["Treat NewEngine's error as fatal at service construction; never continue with the nil value","Initialize the engine eagerly at startup and add a synthetic Plan call to the health check"],"tags":["go","nil-safety","constructor","api-misuse"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}