JuliusBrussee/caveman · error
cacheengine: invalid scope
Error message
cacheengine: invalid scope
What it means
Returned by validatePlanRequest (used by both Plan and StartEpoch) when validIdentity(request.Scope, 4096, false) fails. Scope is a required, non-optional identity string: it must be non-empty, within 4096 bytes, and pass the identity character/structure rules the validator enforces (partitionKey=false means blank is not allowed). It names the cache scope the request belongs to.
Source
Thrown at cacheengine/engine.go:270
if err != nil {
return Plan{}, err
}
return Plan{
Decision: DecisionNewEpoch,
Reason: string(cacheguard.DecisionNewEpoch),
ProfileID: profile.ID,
Mode: profile.Mode,
Attribution: profile.Attribution,
PrefixSHA256: result.PrefixSHA256,
EconomicsBasis: economicsBasis,
KeyShardCount: 1,
Warnings: warnings,
}, nil
}
func validatePlanRequest(request PlanRequest) error {
if !validIdentity(request.Scope, 4096, false) {
return errors.New("cacheengine: invalid scope")
}
if !validIdentity(request.Epoch, 4096, false) {
return errors.New("cacheengine: invalid epoch")
}
if !validIdentity(request.PartitionKey, 4096, true) {
return errors.New("cacheengine: invalid partition key")
}
if request.ExpectedCalls < 0 || request.ExpectedRequestsPerMinute < 0 {
return errors.New("cacheengine: negative traffic expectation")
}
profile := normalizedProfile(request.Profile)
if !validIdentity(profile.ID, 256, false) || !validIdentity(profile.Provider, 64, true) || !validIdentity(profile.OptimizerID, 256, true) {
return errors.New("cacheengine: invalid profile identity")
}
if profile.Mode != ModeUnsupported && profile.Mode != ModeImplicit && profile.Mode != ModeAffinity && profile.Mode != ModeExplicit {
return fmt.Errorf("cacheengine: unknown mode %q", profile.Mode)
}
if profile.Mode != ModeUnsupported {View on GitHub (pinned to 27d5a3981a)
Solutions
- Set PlanRequest.Scope to a stable, non-empty identifier within 4096 bytes (e.g. 'tenant:42:session:abc' trimmed)
- Hash or truncate programmatically derived scopes: use a SHA-256 hex of oversized inputs
- Check the other identity fields in the same validator (Epoch, PartitionKey, profile) — they fail with their own messages
Example fix
// before
req := cacheengine.PlanRequest{Epoch: "e1"} // Scope left empty
// after
req := cacheengine.PlanRequest{Scope: "tenant:42:chat", Epoch: "e1"} Defensive patterns
Strategy: validation
Validate before calling
func safeScope(scope string) string {
if len(scope) > 4096 {
sum := sha256.Sum256([]byte(scope))
return hex.EncodeToString(sum[:])
}
return scope
}
if strings.TrimSpace(req.Scope) == "" {
return errors.New("scope required")
} Try / catch
if _, err := eng.Plan(req); err != nil {
if err.Error() == "cacheengine: invalid scope" {
return fmt.Errorf("scope %q invalid; use non-empty <=4096B identifier", req.Scope)
}
return Plan{}, err
} Prevention
- Generate Scope from a fixed vocabulary (tenant, app, channel) instead of raw user input
- Hash oversized derived scopes to keep them short and stable
When it happens
Trigger: Calling Plan or StartEpoch with an empty Scope, one exceeding 4096 bytes, or containing characters the identity validator rejects (e.g. control characters or invalid UTF-8, per validIdentity's rules).
Common situations: Deriving scope from user IDs or URLs without sanitizing/truncating; forgetting to set Scope when building PlanRequest via struct literal; multi-tenant keys concatenating long tenant metadata past 4096 bytes.
Related errors
- cacheengine: invalid epoch
- token budget must be positive, zero (default), or UnlimitedT
- cachebench: no providers
- cachebench: invalid trace read limits
- cache-replay: -trace and positive request/token/trace/respon
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/815b2ea1feb2d57a.
Report an issue: GitHub.