{"record":{"id":"bf210be85ae34881","repo":"ory/hydra","slug":"server-error-bf210b","errorCode":"server_error","errorMessage":"serialization failure","messagePattern":"serialization failure","errorType":"error_code","errorClass":"fosite.RFC6749Error","httpStatus":500,"severity":"error","filePath":"persistence/sql/persister_oauth2.go","lineNumber":364,"sourceCode":"\treturn err\n}\n\nfunc (p *Persister) deleteSessionByRequestID(ctx context.Context, id string, table tableName) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.deleteSessionByRequestID\")\n\tdefer otelx.End(span, &err)\n\n\terr = p.QueryWithNetwork(ctx).\n\t\tWhere(\"request_id=?\", id).\n\t\tDelete(OAuth2RequestSQL{Table: table}.TableName())\n\tif errors.Is(err, sql.ErrNoRows) {\n\t\treturn errors.WithStack(fosite.ErrNotFound)\n\t}\n\tif err := sqlcon.HandleError(err); err != nil {\n\t\tif errors.Is(err, sqlcon.ErrConcurrentUpdate()) {\n\t\t\treturn fosite.ErrSerializationFailure.WithWrap(err)\n\t\t}\n\t\tif strings.Contains(err.Error(), \"Error 1213\") { // InnoDB Deadlock?\n\t\t\treturn errors.Wrap(fosite.ErrSerializationFailure, err.Error())\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (p *Persister) flushInactiveTokens(ctx context.Context, notAfter time.Time, limit int, batchSize int, table tableName, lifespan time.Duration) (err error) {\n\t/* #nosec G201 table is static */\n\t// The value of notAfter should be the minimum between input parameter and token max expire based on its configured age\n\trequestMaxExpire := time.Now().Add(-lifespan)\n\tif requestMaxExpire.Before(notAfter) {\n\t\tnotAfter = requestMaxExpire\n\t}\n\n\ttotalDeletedCount := 0\n\tfor deletedRecords := batchSize; totalDeletedCount < limit && deletedRecords == batchSize; {\n\t\td := batchSize\n\t\tif limit-totalDeletedCount < batchSize {","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/persistence/sql/persister_oauth2.go#L346-L382","documentation":"deleteSessionByRequestID translates SQL write errors during token deletion (refresh-token rotation via strict/gracefulRefreshRotation, RevokeRefreshToken, RevokeAccessToken) into fosite.ErrSerializationFailure when the driver reports a concurrent-update conflict or an InnoDB deadlock (MySQL \"Error 1213\"). The hint message is \"serialization failure\" with code server_error, telling the OAuth2 layer the DELETE lost a race and should be retried.","triggerScenarios":"Concurrent refresh-token rotation: two refresh requests using the same grant simultaneously, causing racing DELETEs of the old session rows; a deadlocked DELETE on the oauth2 flow/session table under MySQL; two replicas revoking the same token concurrently.","commonSituations":"Aggressive client token refresh (parallel requests), graceful refresh rotation windows with overlapping refreshes, MySQL default isolation with lock contention, load tests hammering refresh endpoint.","solutions":["Retry the token refresh/revocation request with backoff — serialization failures are expected to be transient.","Enable/increase Hydra's SQL retry settings (max_concurrent_retries) so concurrent update errors are retried internally.","Serialize refresh-token usage client-side: never issue parallel refreshes with the same refresh token.","On MySQL, inspect deadlocks (SHOW ENGINE INNODB STATUS) and consider indexing/locking tuning on the session tables."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if errors.Is(err, fosite.ErrSerializationFailure) {\n    time.Sleep(backoff)\n    // retry RevokeRefreshToken / refresh rotation\n}","preventionTips":["Never issue parallel refresh or revocation calls with the same token; single-flight client-side.","Enable SQL retry configuration (max_concurrent_retries) in deployments.","On MySQL, monitor deadlocks and ensure proper indexes on session tables."],"tags":["database","concurrency","deadlock","oauth2","retry"],"backgroundTag":"serialization-failure","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}