schollz/croc · error · Error

close.m || "Sender cancelled"

Error message

close.m || "Sender cancelled"

What it means

Thrown by the recipient after sending 'close-sender' when the sender replies with an 'error' control message (fallback text 'Sender cancelled'). At this point the file data has been received and finalized; the error means the sender-side hit a problem during or immediately after streaming — most often an abort, a socket failure on one of the data connections, or a sender-side chunk-send error — and reported it instead of the expected close-recipient acknowledgement.

Source

Thrown at web/src/protocol/client.ts:797

            totalBytes: totalTransferred,
            totalSize: offer.totalSize,
          });
        });
        const request: RemoteFileRequestWire = {
          CurrentFileChunkRanges: [],
          FilesToTransferCurrentNum: fileIndex,
          MachineID: machineID(),
          ReconnectVersion: 0,
        };
        await sendControl(control, {
          t: "recipientready",
          b: textEncoder.encode(JSON.stringify(request)),
        }, key);
        await receivePromise;
        await sink.finalize();
        await sendControl(control, { t: "close-sender" }, key);
        const close = await receiveControl(control, key);
        if (close.t === "error") throw new Error(close.m || "Sender cancelled");
        if (close.t !== "close-recipient") {
          throw new Error(`Expected sender to close the file, got ${close.t}`);
        }
        callbacks.onStatus?.(`Verifying ${file.path}`);
        await verifySink(sink, file.hash);
        await sink.commit();
        totalTransferred = beforeFile + file.size;
        callbacks.onFileComplete?.(file.path);
      } catch (error) {
        await sink.abort();
        throw error;
      }
    }

    await sendControl(control, { t: "finished" }, key);
    const finishedMessage = await receiveControl(control, key);
    if (finishedMessage.t !== "finished") {
      throw new Error(`Expected transfer completion, got ${finishedMessage.t}`);

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Read the forwarded message text — it is the sender's original error string and names the real cause
  2. Check sender-side network stability; data sockets carry the bulk traffic and fail first on bad links
  3. Verify the sender's files are still readable during streaming (no deletion, no permission changes)
  4. Retry the transfer; transient socket drops on the sender side are the most common cause
Defensive patterns

Strategy: try-catch

Type guard

function isSenderCancelled(e: unknown): boolean {
  return e instanceof Error && /Sender cancelled/.test(e.message);
}

Try / catch

catch (e) {
  if (e instanceof Error && /Sender cancelled/.test(e.message)) {
    // sender failed mid-file: read forwarded text, retry transfer from scratch (no resume support)
  }
  throw e;
}

Prevention

When it happens

Trigger: Sender's sendFileData throws mid-stream (socket.send rejection, AbortSignal fired, file read error) and reportPeerError forwards it; sender user cancels during upload; a data socket drops and the sender aborts before finishing the file; sender-side storage/File slice() fails.

Common situations: Unstable sender uplink dropping a data connection; sender cancels at the end of a large file; sender's file becomes unreadable (moved/deleted/permission changed) between hashing and streaming; sender browser throttles the tab and a socket times out.

Related errors


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/ba36493345b93cfe. Report an issue: GitHub.