block/buzz · info · DOMException

Media fetch cancelled

Error message

Media fetch cancelled

What it means

fetchMediaBytes downloads media bytes via the Tauri command `fetch_media_bytes` and accepts an optional AbortSignal for cancellation. If the signal is already aborted at entry, it throws a DOMException AbortError named 'Media fetch cancelled' instead of making the IPC call. This is intentional fast-fail semantics so callers can treat it uniformly with mid-flight cancellations.

Source

Thrown at desktop/src/shared/api/tauriMedia.ts:90

 */
export async function pickAndUploadImage(): Promise<BlobDescriptor | null> {
  return invokeTauri<BlobDescriptor | null>("pick_and_upload_image", {});
}

/**
 * Fetch relay media bytes over IPC (Rust reqwest, VPN-tunneled).
 *
 * Used by the composer image editor: wrapping the bytes in a same-origin
 * `blob:` URL gives the canvas pixel access without CORS, so the media
 * proxy needs no special headers. The Rust side enforces the same URL
 * validation and size cap as the download commands.
 */
export async function fetchMediaBytes(
  url: string,
  signal?: AbortSignal,
): Promise<Uint8Array<ArrayBuffer>> {
  if (signal?.aborted) {
    throw new DOMException("Media fetch cancelled", "AbortError");
  }

  const requestId = signal ? crypto.randomUUID() : undefined;
  // The Rust command replies with `tauri::ipc::Response`, so the bytes
  // arrive as a raw ArrayBuffer rather than a JSON number array.
  const request = invokeTauri<ArrayBuffer>("fetch_media_bytes", {
    requestId,
    url,
  });
  if (!signal || !requestId) return new Uint8Array(await request);

  let rejectCancellation: ((reason?: unknown) => void) | undefined;
  const cancellation = new Promise<never>((_resolve, reject) => {
    rejectCancellation = reject;
  });
  const onAbort = () => {
    void invokeTauri("cancel_media_fetch", { requestId })
      .catch(() => undefined)

View on GitHub (pinned to dad5a33865)

Solutions

  1. Check signal.aborted before calling fetchMediaBytes and skip the call if already aborted
  2. Create a fresh AbortController per fetch instead of reusing a shared one that may already be aborted
  3. Catch AbortError (e.name === 'AbortError') and treat it as a benign no-op rather than logging it as a failure

Example fix

// before
const bytes = await fetchMediaBytes(url, controller.signal);
// after
if (controller.signal.aborted) return;
try {
  const bytes = await fetchMediaBytes(url, controller.signal);
} catch (e) {
  if ((e as DOMException).name === 'AbortError') return; // benign cancellation
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (signal?.aborted) return; // skip the fetch entirely

Type guard

function isAbortError(e: unknown): e is DOMException {
  return e instanceof DOMException && e.name === 'AbortError';
}

Try / catch

try {
  const bytes = await fetchMediaBytes(url, signal);
} catch (e) {
  if (isAbortError(e)) return null; // benign cancellation
  throw e;
}

Prevention

When it happens

Trigger: Calling fetchMediaBytes (directly or via load/bytes helpers) with an AbortSignal that has already been aborted — e.g. a component unmounted before the download started, or a race where abort() fires between scheduling the call and entering the function.

Common situations: React effect cleanup aborts a controller before the async media fetch begins; a user closes an attachment viewer while its media is queued; parallel loads share one controller and one completes first.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05). Data as JSON: /api/errors/c487b9a390c6f9f8. Report an issue: GitHub.