{"record":{"id":"d761920fc91088b0","repo":"tonhowtf/omniget","slug":"send-cancelled-while-waiting-for-ok-mod","errorCode":null,"errorMessage":"Send cancelled while waiting for OK","messagePattern":"Send cancelled while waiting for OK","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"src-tauri/src/platforms/p2p/mod.rs","lineNumber":294,"sourceCode":"        }\n    };\n\n    check_relay_error(&ready)?;\n    if ready != \"READY\" {\n        anyhow::bail!(\"Unexpected relay response: {}\", ready);\n    }\n\n    *session.status.lock().await = \"connected\".to_string();\n    tracing::info!(\"[p2p] receiver connected\");\n\n    let header = format!(\"{}\\n{}\\n\", session.file_name, session.file_size);\n    write_half.write_all(header.as_bytes()).await?;\n    write_half.flush().await?;\n\n    let ok_response = tokio::select! {\n        line = read_line(&mut reader) => line?,\n        _ = cancel.cancelled() => {\n            anyhow::bail!(\"Send cancelled while waiting for OK\");\n        }\n    };\n\n    if ok_response != \"OK\" {\n        anyhow::bail!(\"Receiver rejected transfer: {}\", ok_response);\n    }\n\n    *session.status.lock().await = \"transferring\".to_string();\n    tracing::info!(\n        \"[p2p] transferring: {} ({} bytes)\",\n        session.file_name,\n        session.file_size\n    );\n\n    let mut file = File::open(&session.file_path).await?;\n    let mut buf = vec![0u8; CHUNK_SIZE];\n    let mut sent: u64 = 0;\n","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/src/platforms/p2p/mod.rs#L276-L312","documentation":"After sending the file name/size header, the sender waits for the receiver's acceptance line via tokio::select!; if cancel.cancelled() wins the race before the OK arrives, run_sender bails with \"Send cancelled while waiting for OK\". Cooperative cancellation during the consent phase.","triggerScenarios":"cancel_token.cancel() is called while the sender is blocked awaiting the receiver's OK/OK-response after transmitting the header.","commonSituations":"User cancels while the receiver is still looking at the accept prompt; UI-level timeout cancels the session; app shutdown.","solutions":["Treat as expected control flow — mark the session cancelled and close the socket.","If unexpected, audit token clones and UI timeout logic that may cancel too aggressively.","Surface a distinct UI state ('cancelled before accept') so users know the transfer never started."],"exampleFix":"// before\nrun_sender(session).await?;\n// after\nif let Err(e) = run_sender(session).await {\n    if e.to_string().contains(\"cancelled\") { session.status.lock().await = \"cancelled\".into(); }\n    else { return Err(e); }\n}","handlingStrategy":"try-catch","validationCode":"if cancel.is_cancelled() { anyhow::bail!(\"Session already cancelled\"); }","typeGuard":null,"tryCatchPattern":"match run_sender(session.clone(), cancel.clone()).await {\n    Err(e) if e.to_string().contains(\"cancelled while waiting for OK\") => {\n        session.status.lock().await = \"cancelled-before-accept\".into();\n    }\n    other => other?,\n}","preventionTips":["Don't wrap run_sender in coarse timeouts that cancel during the consent phase.","Keep cancel tokens per-session and document who may cancel them.","Show the receiver prompt's pending state in the sender UI before cancelling stale sessions.","Cancel explicitly when the user backs out, and close the relay socket after."],"tags":["cancellation","p2p","send","control-flow"],"backgroundTag":"invalid-state-transition","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}