{"record":{"id":"d5085941b72d3c5d","repo":"JuliusBrussee/caveman","slug":"cacheengine-invalid-epoch","errorCode":null,"errorMessage":"cacheengine: invalid epoch","messagePattern":"cacheengine: invalid epoch","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/engine.go","lineNumber":273,"sourceCode":"\treturn Plan{\n\t\tDecision:       DecisionNewEpoch,\n\t\tReason:         string(cacheguard.DecisionNewEpoch),\n\t\tProfileID:      profile.ID,\n\t\tMode:           profile.Mode,\n\t\tAttribution:    profile.Attribution,\n\t\tPrefixSHA256:   result.PrefixSHA256,\n\t\tEconomicsBasis: economicsBasis,\n\t\tKeyShardCount:  1,\n\t\tWarnings:       warnings,\n\t}, nil\n}\n\nfunc validatePlanRequest(request PlanRequest) error {\n\tif !validIdentity(request.Scope, 4096, false) {\n\t\treturn errors.New(\"cacheengine: invalid scope\")\n\t}\n\tif !validIdentity(request.Epoch, 4096, false) {\n\t\treturn errors.New(\"cacheengine: invalid epoch\")\n\t}\n\tif !validIdentity(request.PartitionKey, 4096, true) {\n\t\treturn errors.New(\"cacheengine: invalid partition key\")\n\t}\n\tif request.ExpectedCalls < 0 || request.ExpectedRequestsPerMinute < 0 {\n\t\treturn errors.New(\"cacheengine: negative traffic expectation\")\n\t}\n\tprofile := normalizedProfile(request.Profile)\n\tif !validIdentity(profile.ID, 256, false) || !validIdentity(profile.Provider, 64, true) || !validIdentity(profile.OptimizerID, 256, true) {\n\t\treturn errors.New(\"cacheengine: invalid profile identity\")\n\t}\n\tif profile.Mode != ModeUnsupported && profile.Mode != ModeImplicit && profile.Mode != ModeAffinity && profile.Mode != ModeExplicit {\n\t\treturn fmt.Errorf(\"cacheengine: unknown mode %q\", profile.Mode)\n\t}\n\tif profile.Mode != ModeUnsupported {\n\t\tif profile.MaxBreakpoints <= 0 || profile.MinPrefixTokens < 0 || profile.MaxRPMPerKey < 0 || profile.TTL < 0 {\n\t\t\treturn errors.New(\"cacheengine: invalid cache thresholds\")\n\t\t}","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/engine.go#L255-L291","documentation":"Returned by validatePlanRequest when validIdentity(request.Epoch, 4096, false) fails. Epoch is a required identity string (non-empty, <=4096 bytes, structurally valid) that names the prefix-generation an epoch belongs to; it keys the frozen prefix state that StartEpoch installs and Plan reasons about.","triggerScenarios":"Calling Plan or StartEpoch with an empty Epoch, an Epoch longer than 4096 bytes, or one containing characters rejected by the identity rules.","commonSituations":"Using an incrementing counter that was never initialized (zero value string is empty); epoch derived from a timestamp plus long context that overflows the limit; migration code that renamed fields and stopped populating Epoch.","solutions":["Always populate PlanRequest.Epoch with a non-empty stable identifier, e.g. 'v1' or a content hash of the prefix","Bump the epoch deliberately whenever the stable prefix content changes, so caches re-freeze","Keep it under 4096 bytes and within the identity character rules"],"exampleFix":"// before\neng.StartEpoch(cacheengine.PlanRequest{Scope: \"s\"}) // Epoch empty\n\n// after\neng.StartEpoch(cacheengine.PlanRequest{Scope: \"s\", Epoch: \"prefix-sha256:9f2c...\"})","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(req.Epoch) == \"\" || len(req.Epoch) > 4096 {\n\treturn errors.New(\"epoch must be a non-empty identifier within 4096 bytes\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := eng.StartEpoch(req); err != nil {\n\tif err.Error() == \"cacheengine: invalid epoch\" {\n\t\treturn fmt.Errorf(\"epoch %q invalid; set a stable generation id\", req.Epoch)\n\t}\n\treturn Plan{}, err\n}","preventionTips":["Derive the epoch from a digest of the stable prefix so it changes exactly when content changes","Fail fast on empty epoch in your request builder — the zero-value string always trips this"],"tags":["go","validation","api-misuse","configuration"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}