{"record":{"id":"136c66e483587435","repo":"t8y2/dbx","slug":"tdengine-operation-was-cancelled","errorCode":null,"errorMessage":"TDengine operation was cancelled","messagePattern":"TDengine operation was cancelled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"agents/drivers/tdengine/src/driver.rs","lineNumber":811,"sourceCode":"}\n\nasync fn cancellable<T, F>(token: &CancellationToken, timeout_secs: u64, future: F) -> Result<T>\nwhere\n    F: Future<Output = taos::RawResult<T>>,\n{\n    tokio::pin!(future);\n    let operation = async {\n        if timeout_secs == 0 {\n            future.await.map_err(anyhow::Error::from)\n        } else {\n            timeout(Duration::from_secs(timeout_secs), future)\n                .await\n                .map_err(|_| anyhow!(\"TDengine operation timed out after {timeout_secs} seconds\"))?\n                .map_err(anyhow::Error::from)\n        }\n    };\n    tokio::select! {\n        _ = token.cancelled() => bail!(\"TDengine operation was cancelled\"),\n        result = operation => result,\n    }\n}\n\nfn effective_database<'a>(requested: &'a str, current: &'a str) -> Result<&'a str> {\n    let requested = requested.trim();\n    if !requested.is_empty() {\n        return validate_database_name(requested);\n    }\n    let current = current.trim();\n    if current.is_empty() {\n        bail!(\"TDengine database is required\");\n    }\n    validate_database_name(current)\n}\n\nfn validate_database_name(value: &str) -> Result<&str> {\n    let value = value.trim();","sourceCodeStart":793,"sourceCodeEnd":829,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/tdengine/src/driver.rs#L793-L829","documentation":"cancellable races a TDengine operation against a CancellationToken and the timeout. When the token fires before the operation completes, the driver aborts the await and returns 'TDengine operation was cancelled'. It is the driver's uniform cancellation signal, not a connector fault.","triggerScenarios":"The caller's CancellationToken is cancelled while any wrapped operation is in flight — execute_statements, use_database, start_cursor, next_raw_row, query_scalar_string — e.g. a request timeout/shutdown triggers token.cancel().","commonSituations":"HTTP request aborted by the client, agent shutdown mid-query, or an outer timeout layer cancelling the shared token while a slow query runs.","solutions":["Verify the cancellation was intended (shutdown/timeout) — this error usually means an upstream caller cancelled, so propagate it as cancelled, not retryable","If it fires unexpectedly, audit shared tokens: ensure long queries get their own token not tied to short request lifecycles","Retry with a fresh token if the operation must complete despite the earlier cancellation"],"exampleFix":"// before\nlet token = shared_request_token.clone();\ndriver.execute_query(&conn, sql, &token, 600).await?;\n// after (don't tie long queries to short-lived request tokens)\nlet token = CancellationToken::new();\ndriver.execute_query(&conn, sql, &token, 600).await?;","handlingStrategy":"try-catch","validationCode":"if token.is_cancelled() { return Err(anyhow!(\"already cancelled\")); } // check before starting long ops","typeGuard":null,"tryCatchPattern":"match driver.execute_query(&conn, sql, &token, timeout).await {\n    Err(e) if e.to_string().contains(\"was cancelled\") => {/* expected cancellation: log and return 499-style */},\n    other => other,\n}","preventionTips":["Give long operations their own tokens","Don't tie queries to short request lifecycles","Distinguish cancelled from failed in monitoring"],"tags":["rust","tdengine","cancellation","async","timeout"],"backgroundTag":"operation-cancelled","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}