JuliusBrussee/caveman · error
session-value artifact has no features or actions
Error message
session-value artifact has no features or actions
What it means
The artifact must carry exactly one FeatureSpec per name in the stable metadata vocabulary returned by SessionValueFeatureNames() (21 sorted names today, from audio to user_turns) plus at least one action. A FeatureSpecs array of the wrong length (features added or removed since training) or an empty Actions list fails here, because the evaluator aligns models by position.
Source
Thrown at proxy/routing/session_value.go:192
if !validCompactPoolHash(artifact.CandidatePoolHash) || artifact.CandidatePoolHash != candidatePoolHash {
return errors.New("session-value artifact candidate pool mismatch")
}
if !validSHA256Ref(artifact.TrainingManifestHash) || strings.TrimSpace(artifact.TrainingExtractor) == "" || strings.TrimSpace(artifact.OutcomeContractVersion) == "" {
return errors.New("session-value artifact training lineage invalid")
}
if artifact.ValidFrom.IsZero() || artifact.ValidUntil.IsZero() || !artifact.ValidUntil.After(artifact.ValidFrom) || now.Before(artifact.ValidFrom) || !now.Before(artifact.ValidUntil) {
return errors.New("session-value artifact outside validity window")
}
if artifact.RollbackParentHash != "" && (!validSHA256Ref(artifact.RollbackParentHash) || artifact.RollbackParentHash == artifact.ArtifactHash) {
return errors.New("session-value artifact rollback lineage invalid")
}
if !finite(artifact.QualityUncertaintyZ) || artifact.QualityUncertaintyZ <= 0 || artifact.QualityUncertaintyZ > 5 ||
!finite(artifact.MaxInversePropensity) || artifact.MaxInversePropensity < 1 || artifact.MaxInversePropensity > 100 {
return errors.New("session-value artifact confidence policy invalid")
}
featureNames := SessionValueFeatureNames()
if len(artifact.FeatureSpecs) != len(featureNames) || len(artifact.Actions) == 0 {
return errors.New("session-value artifact has no features or actions")
}
for i, spec := range artifact.FeatureSpecs {
if spec.Name != featureNames[i] || (i > 0 && artifact.FeatureSpecs[i-1].Name >= spec.Name) {
return errors.New("session-value artifact feature vocabulary or order invalid")
}
if spec.Name == "turn_index" && !spec.Required {
return errors.New("session-value artifact must require turn_index")
}
if !finite(spec.Mean) || !finite(spec.Scale) || spec.Scale <= 0 || !finite(spec.Min) || !finite(spec.Max) || spec.Min < 0 || spec.Max < spec.Min {
return fmt.Errorf("session-value feature %q bounds invalid", spec.Name)
}
}
seenActions := map[string]struct{}{}
artifactPool := make([]Candidate, 0, len(artifact.Actions))
for i, action := range artifact.Actions {
wantID := CandidateActionID(Candidate{Provider: action.Provider, Model: action.Model, Effort: action.Effort})
if wantID == "" || action.ActionID != wantID || (i > 0 && artifact.Actions[i-1].ActionID >= action.ActionID) {
return errors.New("session-value artifact action identity or order invalid")View on GitHub (pinned to 766dce6b13)
Solutions
- Regenerate the artifact against the current router's SessionValueFeatureNames() so lengths match.
- Lock trainer and router to the same vocabulary version and retrain whenever the vocabulary changes.
- Ensure at least one candidate action (provider/model/effort) is included when the artifact is produced.
Example fix
// before artifact.FeatureSpecs = trainOnLegacyVocabulary() // 20 specs, router expects 21 artifact.Actions = nil // after artifact.FeatureSpecs = specsFor(routing.SessionValueFeatureNames()) // exactly 21, current names artifact.Actions = trainActions(currentCandidatePool()) // >= 1 action
Defensive patterns
Strategy: validation
Validate before calling
// Before validation: compare collection sizes against the live vocabulary.
wanted := routing.SessionValueFeatureNames() // authoritative length and names
if len(artifact.FeatureSpecs) != len(wanted) {
return fmt.Errorf("artifact has %d feature specs, router vocabulary has %d; regenerate after vocabulary changes",
len(artifact.FeatureSpecs), len(wanted))
}
if len(artifact.Actions) == 0 {
return errors.New("artifact has no actions; not loadable")
} Prevention
- Regenerate artifacts whenever the router's feature vocabulary changes; treat vocabulary changes as breaking.
- Pin trainer and router versions together in one release unit.
- Refuse to train against an empty candidate pool so Actions is never empty.
When it happens
Trigger: The router's feature vocabulary changed (feature added/removed) while the artifact was trained on the old set; an artifact with specs but zero Actions; JSON arrays emptied or truncated by generation tooling.
Common situations: Router upgrade introducing a new metadata feature; a trainer that filters out features it considers unused; an artifact template whose arrays default to empty.
Related errors
- session-value artifact feature vocabulary or order invalid
- session-value artifact must require turn_index
- session-value artifact schema mismatch
- session-value artifact tenant scope mismatch
- session-value artifact version identity invalid
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/5a914f998d3278e1.
Report an issue: GitHub.