{"record":{"id":"5bfa468d719c107e","repo":"JuliusBrussee/caveman","slug":"cacheengine-no-stable-prefix","errorCode":null,"errorMessage":"cacheengine: no stable prefix","messagePattern":"cacheengine: no stable prefix","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cacheengine/engine.go","lineNumber":239,"sourceCode":"}\n\n// StartEpoch explicitly replaces frozen prefix state for one scope/profile epoch.\nfunc (e *Engine) StartEpoch(request PlanRequest) (Plan, error) {\n\tif e == nil || e.guard == 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\tprefix, stableSegments, err := stablePrefix(request.Segments, e.maxStablePrefixBytes)\n\tif err != nil {\n\t\treturn Plan{}, err\n\t}\n\tif len(stableSegments) == 0 {\n\t\treturn Plan{}, errors.New(\"cacheengine: no stable prefix\")\n\t}\n\tif len(cacheguard.DetectVolatile(prefix)) > 0 {\n\t\treturn Plan{}, errors.New(\"cacheengine: volatile content cannot start stable epoch\")\n\t}\n\tprofile := normalizedProfile(request.Profile)\n\teconomicsBasis := \"modeled_input_rate_units\"\n\tvar warnings []string\n\tif !profile.EconomicsKnown {\n\t\teconomicsBasis = \"unavailable\"\n\t\twarnings = []string{\"cache_economics_unavailable\"}\n\t}\n\tresult, err := e.guard.StartNewEpoch(epochKey(request.Scope, request.Epoch, profile.ID), prefix)\n\tif err != nil {\n\t\treturn Plan{}, err\n\t}\n\treturn Plan{\n\t\tDecision:       DecisionNewEpoch,\n\t\tReason:         string(cacheguard.DecisionNewEpoch),","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/engine.go#L221-L257","documentation":"Returned by (*Engine).StartEpoch when stablePrefix(request.Segments, e.maxStablePrefixBytes) yields zero stable segments. An epoch must begin from a non-empty stable prefix — content that is marked stable and within the MaxStablePrefixBytes budget — because the entire epoch's cache boundaries are anchored to that prefix.","triggerScenarios":"Calling StartEpoch where all segments are volatile/unstable, the segments slice is empty, or every candidate stable segment was dropped for exceeding the max stable prefix byte budget (default defaultInputByteLimit when MaxStablePrefixBytes is unset).","commonSituations":"Prompts where the leading content is timestamps/UUIDs (nothing stable at the head); segment metadata incorrectly labeling content as unstable; huge system prompts exceeding the configured MaxStablePrefixBytes; calling StartEpoch before segments are assembled.","solutions":["Ensure the head of the prompt contains at least one stable segment (static system instructions) before volatile content","Verify segment stability flags are set correctly for your pipeline","If the stable content exceeds the byte budget, raise Config.MaxStablePrefixBytes or split the prefix","If no stable prefix exists, skip StartEpoch and use Plan's pass-through/observe-only path instead"],"exampleFix":"// before\nplan, err := eng.StartEpoch(cacheengine.PlanRequest{\n    Segments: []cacheengine.Segment{{Text: now(), Stable: false}}, // nothing stable\n})\n\n// after\nplan, err := eng.StartEpoch(cacheengine.PlanRequest{\n    Segments: []cacheengine.Segment{\n        {Text: systemPrompt, Stable: true}, // stable head\n        {Text: userInput, Stable: false},\n    },\n})","handlingStrategy":"validation","validationCode":"stableBytes := 0\nfor _, s := range req.Segments {\n\tif s.Stable {\n\t\tstableBytes += len(s.Text)\n\t}\n}\nif stableBytes == 0 {\n\treturn errors.New(\"no stable segments; skip StartEpoch and use Plan pass-through\")\n}","typeGuard":"func hasStablePrefix(segments []cacheengine.Segment) bool {\n\tfor _, s := range segments {\n\t\tif s.Stable && len(s.Text) > 0 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","tryCatchPattern":"if _, err := eng.StartEpoch(req); err != nil {\n\tif err.Error() == \"cacheengine: no stable prefix\" {\n\t\t// acceptable: fall back to Plan-only pass-through for this request\n\t\treturn eng.Plan(req)\n\t}\n\treturn Plan{}, err\n}","preventionTips":["Design prompts with a static leading block (system instructions) and mark only it Stable","Keep stable prefix bytes under Config.MaxStablePrefixBytes; raise it deliberately if prompts are large"],"tags":["go","prompt-cache","validation","api-misuse"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}