{"record":{"id":"5366339ba798935a","repo":"databendlabs/databend","slug":"mysql-handler-should-be-authed-when-call","errorCode":null,"errorMessage":"mysql handler should be authed when call","messagePattern":"mysql handler should be authed when call","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/service/src/servers/mysql/mysql_interactive_worker.rs","lineNumber":604,"sourceCode":"        salt: [u8; 20],\n        keep_alive_task: KeepAliveTask,\n    ) -> InteractiveWorker {\n        InteractiveWorker {\n            version: format!(\"{MYSQL_VERSION}-{}\", version.commit_detail),\n            base: InteractiveWorkerBase { session, version },\n            salt,\n            client_addr,\n            keep_alive_task,\n        }\n    }\n\n    fn start_keep_alive(&mut self) {\n        let session = &self.base.session;\n        let tenant = session.get_current_tenant();\n        let session_id = session.get_id();\n        let user_name = session\n            .get_current_user()\n            .expect(\"mysql handler should be authed when call\")\n            .name;\n        let (shutdown_tx, mut shutdown_rx) = oneshot::channel();\n\n        let task = databend_common_base::runtime::spawn(async move {\n            loop {\n                UserApiProvider::instance()\n                    .client_session_api(&tenant)\n                    .upsert_client_session_id(\n                        &user_name,\n                        &session_id,\n                        Duration::from_secs(3600 + 600),\n                    )\n                    .await\n                    .ok();\n                tokio::select! {\n                    _ = tokio::time::sleep(Duration::from_secs(3600)) => {},\n                    _ = &mut shutdown_rx => break,\n                }","sourceCodeStart":586,"sourceCodeEnd":622,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/service/src/servers/mysql/mysql_interactive_worker.rs#L586-L622","documentation":"start_keep_alive in the MySQL interactive worker reads the authenticated user from the session with get_current_user().expect(...). The invariant is that keep-alive only starts after authentication succeeded, so current user must exist. The panic fires when the worker reaches start_keep_alive without a completed auth — an auth state-machine violation.","triggerScenarios":"on_query invoked before the MySQL handshake/auth phase completed; auth succeeded but the user was cleared from the session; internal misordering of worker lifecycle callbacks.","commonSituations":"Client connecting with broken auth flow (e.g. auth plugin negotiation failing partially) yet issuing a query; proxy/connector sending COM_QUERY before handshake finish; version-specific regression in session user management.","solutions":["Reconnect with a properly working MySQL client/driver and correct credentials.","Check proxy configs (e.g. connection poolers) that may skip or replay handshake steps.","Patch to propagate an error instead of panicking: return early / send ERR packet when user is None.","Capture version and stack trace and report; indicates an auth lifecycle bug."],"exampleFix":"// before\nlet user_name = session.get_current_user().expect(\"mysql handler should be authed when call\").name;\n// after\nlet user_name = match session.get_current_user() {\n    Some(u) => u.name,\n    None => { self.kill(); return; }\n};","handlingStrategy":"try-catch","validationCode":"// client: ensure handshake completed before sending queries\nif (!connection.is_authenticated()) await connection.completeHandshake();","typeGuard":"fn is_authed(session: &Session) -> bool { session.get_current_user().is_some() }","tryCatchPattern":"if let Some(user) = session.get_current_user() { /* start keep alive */ } else { /* abort worker gracefully */ }","preventionTips":["Use MySQL clients that complete the auth handshake before COM_QUERY","Check connection poolers don't replay partial handshakes","Keep databend-query updated for auth lifecycle fixes","Return an ERR packet instead of panicking in worker code"],"tags":["rust","panic","mysql","authentication","session"],"backgroundTag":"authentication-required","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}