JuliusBrussee/caveman · warning
cacheengine: no stable prefix
Error message
cacheengine: no stable prefix
What it means
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.
Source
Thrown at cacheengine/engine.go:239
}
// StartEpoch explicitly replaces frozen prefix state for one scope/profile epoch.
func (e *Engine) StartEpoch(request PlanRequest) (Plan, error) {
if e == nil || e.guard == nil {
return Plan{}, errors.New("cacheengine: nil engine")
}
if e.configErr != nil {
return Plan{}, e.configErr
}
if err := validatePlanRequest(request); err != nil {
return Plan{}, err
}
prefix, stableSegments, err := stablePrefix(request.Segments, e.maxStablePrefixBytes)
if err != nil {
return Plan{}, err
}
if len(stableSegments) == 0 {
return Plan{}, errors.New("cacheengine: no stable prefix")
}
if len(cacheguard.DetectVolatile(prefix)) > 0 {
return Plan{}, errors.New("cacheengine: volatile content cannot start stable epoch")
}
profile := normalizedProfile(request.Profile)
economicsBasis := "modeled_input_rate_units"
var warnings []string
if !profile.EconomicsKnown {
economicsBasis = "unavailable"
warnings = []string{"cache_economics_unavailable"}
}
result, err := e.guard.StartNewEpoch(epochKey(request.Scope, request.Epoch, profile.ID), prefix)
if err != nil {
return Plan{}, err
}
return Plan{
Decision: DecisionNewEpoch,
Reason: string(cacheguard.DecisionNewEpoch),View on GitHub (pinned to 27d5a3981a)
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
Example fix
// before
plan, err := eng.StartEpoch(cacheengine.PlanRequest{
Segments: []cacheengine.Segment{{Text: now(), Stable: false}}, // nothing stable
})
// after
plan, err := eng.StartEpoch(cacheengine.PlanRequest{
Segments: []cacheengine.Segment{
{Text: systemPrompt, Stable: true}, // stable head
{Text: userInput, Stable: false},
},
}) Defensive patterns
Strategy: validation
Validate before calling
stableBytes := 0
for _, s := range req.Segments {
if s.Stable {
stableBytes += len(s.Text)
}
}
if stableBytes == 0 {
return errors.New("no stable segments; skip StartEpoch and use Plan pass-through")
} Type guard
func hasStablePrefix(segments []cacheengine.Segment) bool {
for _, s := range segments {
if s.Stable && len(s.Text) > 0 {
return true
}
}
return false
} Try / catch
if _, err := eng.StartEpoch(req); err != nil {
if err.Error() == "cacheengine: no stable prefix" {
// acceptable: fall back to Plan-only pass-through for this request
return eng.Plan(req)
}
return Plan{}, err
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- cacheengine: volatile content cannot start stable epoch
- cacheengine: invalid scope
- cacheengine: invalid epoch
- history requires memory id
- token budget must be positive, zero (default), or UnlimitedT
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/5bfa468d719c107e.
Report an issue: GitHub.