JuliusBrussee/caveman · warning
cacheengine: volatile content cannot start stable epoch
Error message
cacheengine: volatile content cannot start stable epoch
What it means
Returned by (*Engine).StartEpoch when cacheguard.DetectVolatile reports volatile content within the stable prefix bytes. Even though a non-empty stable prefix was found, its bytes contain patterns the cache guard classifies as volatile (timestamps, random IDs, and similar ever-changing content), and anchoring a cache epoch on such content would poison cache hit rates.
Source
Thrown at cacheengine/engine.go:242
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),
ProfileID: profile.ID,
Mode: profile.Mode,
Attribution: profile.Attribution,View on GitHub (pinned to 27d5a3981a)
Solutions
- Move volatile content (dates, IDs, nonces) out of the leading stable prefix into later unstable segments
- Fix segment metadata: content with detectable volatility must not be flagged stable
- Hoist truly static instructions (persona, tool docs) to the very start of the prompt and keep them free of interpolated values
- Re-run and inspect which offsets DetectVolatile flags to locate the offending bytes
Example fix
// before
segments := []cacheengine.Segment{
{Text: "Assistant v2. Context date: " + time.Now().Format(time.RFC3339), Stable: true},
}
// after
segments := []cacheengine.Segment{
{Text: "Assistant v2.", Stable: true},
{Text: "Context date: " + time.Now().Format(time.RFC3339), Stable: false},
} Defensive patterns
Strategy: validation
Validate before calling
prefix := stablePrefixBytes(req.Segments)
if volatiles := cacheguard.DetectVolatile(prefix); len(volatiles) > 0 {
return fmt.Errorf("stable prefix contains volatile content at %v; restructure prompt", volatiles)
} Type guard
func prefixIsStable(prefix []byte) bool {
return len(prefix) > 0 && len(cacheguard.DetectVolatile(prefix)) == 0
} Try / catch
if _, err := eng.StartEpoch(req); err != nil {
if err.Error() == "cacheengine: volatile content cannot start stable epoch" {
return Plan{}, fmt.Errorf("rebuild prompt: move timestamps/IDs below the stable prefix")
}
return Plan{}, err
} Prevention
- Never interpolate dates, random IDs, or per-request values into stable-flagged segments
- Add a lint step in the prompt builder that runs DetectVolatile over the stable prefix before StartEpoch
When it happens
Trigger: A stable-flagged segment whose text embeds a timestamp, request ID, nonce, or other detectable volatile token at the epoch start. Content copied from logs or generated with Date.now()-style interpolation and marked Stable: true.
Common situations: System prompts templated with the current date; stable segments that include per-session IDs; refactors that moved dynamic fields above the static prefix; LLM scaffolds that interpolate 'today' into instructions.
Related errors
- cacheengine: no stable prefix
- cachebench: nil corpus reader
- message exceeds byte limit
- tool message requires tool_call_id
- cachebench: invalid corpus
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/7fe33858429013e2.
Report an issue: GitHub.