Eazip
@eazip/core

Partial results & errors

Per-file skips, the partial status, and the error taxonomy.

A single broken link shouldn't sink a 200-file export. By default (failOnUrlError: false), a source that fails to fetch or read is skipped — not thrown — and the zip is still built from everything else:

const result = await createZip({ files: urls });

result.status;       // 'completed' | 'partial'
result.errors;        // EazipError[] — one entry per skipped file
result.skippedCount;  // errors.length

Each skip is an EazipError: { code, message, fileIndex?, filename?, cause? }. code is one of LOCAL_SOURCE_FETCH_FAILED (the request failed or returned a non-2xx status) or LOCAL_SOURCE_READ_FAILED (the response couldn't be read as a Blob). Set failOnUrlError: true to make the first such failure reject the whole job instead — useful when every file is required and a partial zip isn't a useful outcome.

If literally every source fails, a partial zip with zero files inside would just be noise — so the job fails outright instead, with EazipValidationError('ALL_SOURCES_FAILED') (the skipped-file list is attached as cause).

This per-file detail is a local-strategy feature. Cloud results also expose errors and skippedCount on ZipResult, but the Public Sessions API only reports a skipped count, not per-file detail — so for cloud jobs errors is always [], and skippedCount is derived from urlCount - fileCount.

Error taxonomy

Everything @eazip/core throws or rejects with extends EazipErrorBase (code, optional status/retryAfterMs; isEazipError() narrows to it). Dedicated subclasses exist only where callers realistically branch on one — every other wire code stays a generic EazipApiError with code preserved:

ClasscodeWhen you'll see it
EazipValidationErrorvaries (EMPTY_INPUT, INVALID_INPUT, ALL_SOURCES_FAILED, NOT_READY, …)Bad input or misuse — synchronous or via job.done, never from the network.
EazipAbortErrorABORT_ERRjob.abort() was called, or the signal you passed fired.
EazipNetworkErrorNETWORK_ERRORfetch itself threw (offline, DNS, CORS preflight failure), or no fetch implementation was found.
EazipChallengeRequiredErrorPUBLIC_APP_CHALLENGE_REQUIREDCloud only — the Public App requires a Turnstile token; carries .challenge.
EazipSessionExpiredErrorSESSION_EXPIREDCloud only — the session aged out before the job finished.
EazipSessionRevokedErrorSESSION_REVOKEDCloud only — the session was revoked server-side.
EazipDownloadExpiredErrorDOWNLOAD_URL_EXPIREDCloud only — a signed download link was used after it expired.
EazipRateLimitErrore.g. PUBLIC_APP_RATE_LIMITEDCloud only — too many requests; carries retryAfterMs.
EazipQuotaErrore.g. QUOTA_EXCEEDEDCloud only — a plan or quota limit was hit.
EazipJobFailedErrorJOB_FAILEDCloud only — the server-side job itself failed.
EazipApiErrorany other wire codeFallback for any API error without a dedicated subclass.
import { createZip, isEazipError, EazipRateLimitError } from '@eazip/core';

try {
  await createZip({ strategy: 'cloud', publicKey, files });
} catch (error) {
  if (error instanceof EazipRateLimitError) {
    console.log('retry after', error.retryAfterMs);
  } else if (isEazipError(error)) {
    console.log(error.code, error.message);
  }
}

See Why Eazip Cloud for the challenge/Turnstile flow behind EazipChallengeRequiredError, and Sessions & resume for the session-expiry errors.

On this page