{"record":{"id":"4bf363f18690cd57","repo":"tonhowtf/omniget","slug":"send-cancelled-while-waiting-for-ok","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/omniget-core/src/platforms/p2p.rs","lineNumber":292,"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":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/p2p.rs#L274-L310","documentation":"After sending the file header (name + size) to the receiver, run_sender waits for an \"OK\" acknowledgement inside a tokio::select! against the cancel token. If cancellation fires while waiting, this error aborts the send. It's cooperative cancellation, distinct from a receiver rejection (which produces error 88).","triggerScenarios":"Cancelling the CancellationToken after run_sender wrote the file header but before the receiver's \"OK\" response arrives.","commonSituations":"User aborts the send at the confirmation screen; receiver is slow to approve and the user gives up; app shutdown cancels the pending negotiation.","solutions":["Treat as an intentional abort: match the message and reset session status instead of logging a failure","If you want graceful shutdown, notify the receiver/relay before cancelling so the half-negotiated session is cleaned up","Restart with a fresh start_send session; the cancelled session cannot be resumed"],"exampleFix":"// before\n_ = cancel.cancelled() => {\n    anyhow::bail!(\"Send cancelled while waiting for OK\");\n}\n// after\n_ = cancel.cancelled() => {\n    *session.status.lock().await = \"cancelled\".to_string();\n    anyhow::bail!(\"Send cancelled while waiting for OK\");\n}","handlingStrategy":"try-catch","validationCode":"// Don't cancel during negotiation unless the user explicitly aborts\ndebug_assert!(!cancel.is_cancelled(), \"token must be live until transfer starts\");","typeGuard":null,"tryCatchPattern":"if let Err(e) = run_sender(&mut session, &cancel).await {\n    if e.to_string().contains(\"cancelled\") {\n        session.status.lock().await = \"cancelled\".into();\n    } else {\n        return Err(e);\n    }\n}","preventionTips":["Wire the cancel button only to visible, user-triggered aborts","Cancel during negotiation should also notify the relay/receiver for cleanup","Keep negotiation timeouts longer than typical human approval delays"],"tags":["p2p","cancellation","sender","handshake"],"backgroundTag":"operation-cancelled","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"}