JuliusBrussee/caveman · error
session-value artifact outside validity window
Error message
session-value artifact outside validity window
What it means
Session-value artifacts are time-boxed: ValidFrom and ValidUntil must both be non-zero, ValidUntil must be strictly after ValidFrom, and the validation time must satisfy ValidFrom <= now < ValidUntil. Outside that window the policy is expired or not yet active, and the router refuses it so no stale policy is served.
Source
Thrown at proxy/routing/session_value.go:181
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")
}
if spec.Name == "turn_index" && !spec.Required {
return errors.New("session-value artifact must require turn_index")View on GitHub (pinned to 766dce6b13)
Solutions
- Issue and deploy a fresh artifact whose window covers the current time before loading it.
- Set the validity window generously relative to your deploy cadence (days, not minutes) and always in UTC.
- If 'now' looks wrong, check the validating host's clock (NTP status) and confirm the trainer writes RFC3339 UTC timestamps.
Example fix
// before
{
"valid_from": "2026-08-01T00:00:00Z",
"valid_until": "2026-08-02T00:00:00Z" // loaded on 2026-08-17: expired
}
// after
{
"valid_from": "2026-08-17T00:00:00Z",
"valid_until": "2026-08-24T00:00:00Z" // reissued window covering deploy
} Defensive patterns
Strategy: validation
Validate before calling
// Before validation: check the window with the same clock semantics.
now := time.Now().UTC()
if artifact.ValidFrom.IsZero() || artifact.ValidUntil.IsZero() ||
!artifact.ValidUntil.After(artifact.ValidFrom) ||
now.Before(artifact.ValidFrom) || !now.Before(artifact.ValidUntil) {
return fmt.Errorf("artifact window %s..%s does not cover %s; issue a fresh artifact",
artifact.ValidFrom.Format(time.RFC3339), artifact.ValidUntil.Format(time.RFC3339), now.Format(time.RFC3339))
} Type guard
func artifactInWindow(a routing.SessionValuePolicyArtifact, now time.Time) bool {
return !a.ValidFrom.IsZero() && !a.ValidUntil.IsZero() &&
a.ValidUntil.After(a.ValidFrom) && !now.Before(a.ValidFrom) && now.Before(a.ValidUntil)
} Prevention
- Issue artifacts with windows sized to your deploy cadence (days), always in UTC RFC3339.
- Automate reissue before expiry instead of relying on operators noticing expiry at load time.
- Keep validating hosts NTP-synced; skew across the fleet can flip this check.
When it happens
Trigger: Loading an expired artifact (now >= ValidUntil) or one whose window has not started yet (now < ValidFrom); zero timestamps from an unstamped generator; an inverted window from a timezone bug; clock skew between the host that sealed the artifact and the host validating it.
Common situations: Router restarted hours or days after artifact issuance when the validity window is short; trainer serializing local-time timestamps instead of UTC RFC3339; NTP drift on the validating host.
Related errors
- session-value artifact schema mismatch
- session-value artifact tenant scope mismatch
- session-value artifact version identity invalid
- session-value artifact candidate pool mismatch
- session-value artifact rollback lineage invalid
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/92de1178c239ce89.
Report an issue: GitHub.