{"record":{"id":"1bfcbebf66e851c9","repo":"netbirdio/netbird","slug":"w-v","errorCode":null,"errorMessage":"%w: %v","messagePattern":"%w: %v","errorType":"exception","errorClass":"ErrDeadlineBeforeEpoch","httpStatus":null,"severity":"warning","filePath":"client/internal/auth/sessionwatch/watcher.go","lineNumber":160,"sourceCode":"// applySessionDeadline forces a zero deadline into the status recorder\n// after a non-nil error).\nfunc (w *Watcher) Update(deadline time.Time) error {\n\tw.mu.Lock()\n\tif w.closed {\n\t\tw.mu.Unlock()\n\t\treturn nil\n\t}\n\n\tif deadline.IsZero() {\n\t\tw.clearLocked()\n\t\treturn nil\n\t}\n\n\tnow := time.Now()\n\tswitch {\n\tcase deadline.Before(time.Unix(0, 0)):\n\t\tw.clearLocked()\n\t\treturn fmt.Errorf(\"%w: %v\", ErrDeadlineBeforeEpoch, deadline)\n\tcase deadline.After(now.Add(maxDeadlineHorizon)):\n\t\tw.clearLocked()\n\t\treturn fmt.Errorf(\"%w: %v\", ErrDeadlineTooFarFuture, deadline)\n\tcase deadline.Before(now.Add(-maxPastHorizon)):\n\t\tw.clearLocked()\n\t\treturn fmt.Errorf(\"%w: %v (now=%v)\", ErrDeadlineInPast, deadline, now)\n\t}\n\n\tif deadline.Equal(w.current) {\n\t\tw.mu.Unlock()\n\t\treturn nil\n\t}\n\n\tw.stopTimerLocked()\n\tw.current = deadline\n\t// Reset every per-deadline guard so a refreshed deadline arms a fresh\n\t// warning cycle: both edge triggers and the user Dismiss decision\n\t// (the user agreed to the old deadline expiring; a new deadline","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/auth/sessionwatch/watcher.go#L142-L178","documentation":"sessionwatch.Watcher.Update rejected the SSO session expiry deadline published by management because it pre-dates the Unix epoch (before 1970-01-01). The watcher first clears its own state and notifies the recorder, then returns the sentinel ErrDeadlineBeforeEpoch wrapped with the offending timestamp. It signals a malformed timestamp on the wire rather than a real session deadline, and is designed for errors.Is matching.","triggerScenarios":"Update(deadline) is called with a value where deadline.Before(time.Unix(0,0)) is true: management serialized a negative expiry, sent an unset field that deserializes to a negative value, or agent/management version skew misinterprets the field (for example a signed vs unsigned or unit mismatch producing negative nanoseconds).","commonSituations":"Management version that predates the session-expiry feature leaving the field unset in a way the agent misreads; protobuf schema drift between agent and management; a management bug setting expiry from an uninitialized variable.","solutions":["Handle it as 'no deadline': the watcher already cleared its state, so push a zero deadline into any other sinks (applySessionDeadline does exactly this on error).","Report the agent and management versions together with the rejected value printed after the colon - the timestamp is malformed on the wire and needs a server-side fix.","Upgrade the management service if it is older than the session-expiry feature; a current server omits the field instead of sending a bogus value.","If self-hosted, inspect the LoginResponse/SyncResponse the server actually emits for the expiry field."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// validate a session deadline before feeding it to the watcher\nfunc validSessionDeadline(d time.Time) bool {\n    if d.IsZero() {\n        return true // clearing is always allowed\n    }\n    return !d.Before(time.Unix(0, 0)) &&\n        d.Before(time.Now().Add(10*365*24*time.Hour)) &&\n        d.After(time.Now().Add(-30*24*time.Hour))\n}","typeGuard":"func isDeadlineSanityErr(err error) bool {\n    return errors.Is(err, sessionwatch.ErrDeadlineBeforeEpoch) ||\n        errors.Is(err, sessionwatch.ErrDeadlineTooFarFuture) ||\n        errors.Is(err, sessionwatch.ErrDeadlineInPast)\n}","tryCatchPattern":"if err := w.Update(deadline); err != nil {\n    if errors.Is(err, sessionwatch.ErrDeadlineBeforeEpoch) {\n        // protocol glitch: watcher already cleared; force zero deadline into\n        // other sinks and log the raw value for a server-side fix\n    }\n}","preventionTips":["Validate timestamps at the boundary where SyncResponse/LoginResponse fields are read, before they reach the watcher.","Keep agent and management versions aligned so the expiry field's semantics match.","Unit-multiply explicitly (time.Unix(sec, 0)) rather than trusting raw int64 fields as time.Time.","Log rejected values with both the deadline and now so skew is diagnosable from one line."],"tags":["session","sso","time","protocol","errors-is"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}