{"record":{"id":"d70c259349242ab2","repo":"instructure/canvas-lms","slug":"auth-cookie-data-present-failed-to-unmarshal-inactivity","errorCode":null,"errorMessage":"[AUTH] Cookie data (present) failed to unmarshal. Inactivity timeout or invalid digest.","messagePattern":"\\[AUTH\\] Cookie data \\(present\\) failed to unmarshal\\. Inactivity timeout or invalid digest\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"app/middleware/enhanced_cookie_store.rb","lineNumber":36,"sourceCode":"# with this program. If not, see <http://www.gnu.org/licenses/>.\n\nclass EnhancedCookieStore < ActionDispatch::Session::EncryptedCookieStore\n  def unmarshal(data, options = {})\n    unmarshalled_data = nil\n    begin\n      unmarshalled_data = super\n    rescue ArgumentError => e\n      # if the data being provided is not formatted in such a way that\n      # we can extract appropriately sized segments from it,\n      # then this is an auth problem (bad cookie), not a real\n      # exception.  We'll return nil as though the cookie\n      # was unauthorized (and it is), and log the failure, but not explode because\n      # handling this as some 4xx is more accurate than a 500.\n      Canvas::Errors.capture_exception(:cookie_store, e, :info)\n      return nil\n    end\n    if unmarshalled_data.nil? && data.present?\n      Rails.logger.warn(\"[AUTH] Cookie data (present) failed to unmarshal. Inactivity timeout or invalid digest.\")\n    end\n    unmarshalled_data\n  end\nend\n","sourceCodeStart":18,"sourceCodeEnd":41,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/middleware/enhanced_cookie_store.rb#L18-L41","documentation":"Canvas's enhanced cookie store decrypts/verifies and unmarshals session cookie data. If a cookie is present but unmarshals to nil — digest invalid, serialized payload corrupt, or session expired by inactivity — it logs this warning instead of raising, and the user gets a fresh empty session.","triggerScenarios":"A present, non-empty cookie's data fails Marshal.load or digest verification in unmarshal — inactivity-timeout expiry, stale cookie after a signing-secret change, or truncated/corrupted cookie data.","commonSituations":"Deploy that changed session store secrets or serialization; idle user past inactivity timeout reusing an old cookie; proxies truncating oversized cookies; cookie tampering; format changes across upgrades.","solutions":["Benign for a single user — they just log in again; confirm new sessions work","If widespread: ensure session secrets/signing keys are consistent across all app servers (no mixed old/new keys mid-rollout)","Check for cookie truncation by proxies (keep session payload small)","Verify no recent change to cookie store serializer/version and that inactivity timeout config matches expectations"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"# Caller-side: treat nil session as anonymous\nsession_data = EnhancedCookieStore.unmarshal(cookie_data)\nuser = session_data.present? ? load_user(session_data) : nil","typeGuard":"def valid_session?(data)\n  data.is_a?(Hash) && data.key?('session_id')\nend","tryCatchPattern":"# The store already swallows errors; callers should just check for nil\nsession = store.unmarshal(data)\nif session.nil?\n  # start a fresh anonymous session and require re-login\nend","preventionTips":["Keep session secrets consistent across all app servers during deploys","Keep cookies small to avoid proxy truncation","Expect inactivity-timeout cookies to be common; never treat nil session as a server error","Rotate secrets with overlap so old cookies still verify during rollout"],"tags":["session","cookies","authentication","rails-middleware"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}