jackwener/OpenCLI · error · CliError

UPLOAD_ERROR

UPLOAD_ERROR

Error message

S3 upload failed: ${getErrorMessage(error)}

What it means

uploadPresignedPdf() POSTs a multipart form (presigned fields plus the PDF blob) to the presigned S3 URL. If fetch rejects before a response arrives, it throws UPLOAD_ERROR 'S3 upload failed: <cause>'. This wraps network-level upload failures to the S3 presigned endpoint, separate from S3 returning an HTTP error.

Source

Thrown at clis/paperreview/utils.js:140

}
export function createUploadForm(urlData, pdfFile) {
    const form = new FormData();
    for (const [key, value] of Object.entries(urlData.presigned_fields ?? {})) {
        form.append(key, value);
    }
    form.append('file', new Blob([new Uint8Array(pdfFile.buffer)], { type: 'application/pdf' }), pdfFile.fileName);
    return form;
}
export async function uploadPresignedPdf(presignedUrl, pdfFile, urlData) {
    let response;
    try {
        response = await fetch(presignedUrl, {
            method: 'POST',
            body: createUploadForm(urlData, pdfFile),
        });
    }
    catch (error) {
        throw new CliError('UPLOAD_ERROR', `S3 upload failed: ${getErrorMessage(error)}`, 'Try again in a moment');
    }
    if (!response.ok) {
        const body = await response.text();
        throw new CliError('UPLOAD_ERROR', body || `S3 upload failed with status ${response.status}.`, 'Try again in a moment');
    }
}
export function summarizeSubmission(options) {
    const { pdfFile, email, venue, token, message, s3Key, dryRun = false, status } = options;
    return {
        status: status ?? (dryRun ? 'dry-run' : 'submitted'),
        file: pdfFile.fileName,
        file_path: pdfFile.resolvedPath,
        size_bytes: pdfFile.sizeBytes,
        email,
        venue,
        token: token ?? '',
        review_url: token ? buildReviewUrl(token) : '',
        message: message ?? '',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the upload — the library's own hint is 'Try again in a moment'; presigned flows are typically safe to re-run
  2. Check connectivity to the bucket host: curl -v <presigned-url-domain>
  3. If behind a proxy/firewall, allow HTTPS to s3 / *.amazonaws.com or set HTTPS_PROXY
  4. Regenerate the presigned URL if it may have expired, then retry immediately

Example fix

// before
await uploadPresignedPdf(presignedUrl, pdfFile, urlData); // fails once on flaky network
// after
for (let attempt = 1; attempt <= 3; attempt++) {
    try { await uploadPresignedPdf(presignedUrl, pdfFile, urlData); break; }
    catch (e) { if (e.code === 'UPLOAD_ERROR' && attempt < 3) await new Promise(r => setTimeout(r, 1000 * attempt)); else throw e; }
}
Defensive patterns

Strategy: retry

Validate before calling

async function canReachS3(presignedUrl) {
    try { new URL(presignedUrl); }
    catch { throw new Error('Malformed presigned URL'); }
    const host = new URL(presignedUrl).hostname;
    try { await fetch(`https://${host}/`, { method: 'HEAD' }); return true; }
    catch { return false; }
}

Try / catch

for (let attempt = 1; attempt <= 3; attempt++) {
    try { await uploadPresignedPdf(presignedUrl, pdfFile, urlData); break; }
    catch (e) {
        if (e?.code === 'UPLOAD_ERROR' && e.message.startsWith('S3 upload failed:') && attempt < 3) {
            await new Promise(r => setTimeout(r, 1000 * 2 ** (attempt - 1)));
        } else throw e;
    }
}

Prevention

When it happens

Trigger: fetch(presignedUrl, { method: 'POST', body: form }) rejecting: no network, DNS failure for the S3/bucket hostname, connection reset mid-upload of a large body, request timeout, TLS error, or FormData/Blob construction failure (e.g. malformed urlData.presigned_fields).

Common situations: Large PDF over a flaky connection dropping mid-upload; corporate proxy blocking PUT/POST to *.amazonaws.com; expired presigned URL causing server-side close; missing NODE_EXTRA_CA_CERTS behind TLS interception.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/94e2237a65f7555d. Report an issue: GitHub.