JuliusBrussee/caveman · error
session-value artifact training lineage invalid
Error message
session-value artifact training lineage invalid
What it means
Training-lineage fields prove what data and contracts produced the artifact: TrainingManifestHash must be a 'sha256:<64 lowercase hex>' reference to the training manifest, and both TrainingExtractor (training_extractor_version) and OutcomeContractVersion must be non-empty. Missing lineage makes the policy unauditable, so validation rejects it.
Source
Thrown at proxy/routing/session_value.go:178
return artifact, nil
}
func ValidateSessionValueArtifact(artifact SessionValuePolicyArtifact, organizationID, projectID, candidatePoolHash string, now time.Time) error {
if artifact.Schema != SessionValueArtifactSchema || artifact.StateSchemaVersion != SessionValueStateSchema {
return errors.New("session-value artifact schema mismatch")
}
if strings.TrimSpace(artifact.OrganizationID) == "" || artifact.OrganizationID != strings.TrimSpace(organizationID) ||
strings.TrimSpace(artifact.ProjectID) == "" || artifact.ProjectID != strings.TrimSpace(projectID) {
return errors.New("session-value artifact tenant scope mismatch")
}
if artifact.PolicyVersion <= 0 || artifact.RouterVersion != SessionValueRouterVersion || strings.TrimSpace(artifact.EstimatorVersion) == "" {
return errors.New("session-value artifact version identity invalid")
}
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")View on GitHub (pinned to 766dce6b13)
Solutions
- Stamp all three fields during training: the manifest digest as 'sha256:' + 64 lowercase hex chars, plus non-empty extractor and outcome-contract versions.
- Run SealSessionValueArtifact after filling lineage so the artifact hash covers the lineage fields.
- Add a trainer-side assertion that lineage fields are non-empty and correctly formatted before sealing.
Example fix
// before
{
"training_manifest_hash": "9f2a...", // missing 'sha256:' prefix
"training_extractor_version": "",
"outcome_contract_version": ""
}
// after
{
"training_manifest_hash": "sha256:9f2a...<64 hex total>",
"training_extractor_version": "feat-ext-v3",
"outcome_contract_version": "outcome-v2"
} Defensive patterns
Strategy: validation
Validate before calling
// Before validation: check lineage format.
var sha256Ref = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
if !sha256Ref.MatchString(artifact.TrainingManifestHash) ||
strings.TrimSpace(artifact.TrainingExtractor) == "" ||
strings.TrimSpace(artifact.OutcomeContractVersion) == "" {
return errors.New("artifact lineage incomplete: need sha256:<64 hex> manifest hash plus extractor and outcome-contract versions")
} Prevention
- Always hash the training manifest during training and store it as 'sha256:' + 64 lowercase hex.
- Make extractor and outcome-contract versions mandatory trainer outputs, failing the training job when unset.
- Seal the artifact only after lineage is complete so the ArtifactHash covers it.
When it happens
Trigger: The artifact generator omitted the manifest hash or stored a bare 64-char digest without the 'sha256:' prefix (or with wrong length/case); training_extractor_version or outcome_contract_version left empty or renamed in the JSON so they unmarshal as empty strings.
Common situations: A trainer refactor dropping lineage fields; a manual or offline training run that skipped the manifest-hashing step; JSON field renames (training_extractor vs training_extractor_version) during schema migration.
Related errors
- session-value artifact rollback lineage invalid
- session-value artifact candidate pool mismatch
- assembly slot {slot_id!r} changed after being declared stabl
- session-value promotion artifact has no actions
- session-value artifact schema mismatch
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/7c2da53fd7f104a1.
Report an issue: GitHub.