yikart/AiToEarn · warning · DOMException

上传已取消

Error message

上传已取消

What it means

DOMException with name 'AbortError' thrown at the start of uploadToOss when opts.signal.aborted is already true — i.e. the caller's AbortController was aborted before the upload began. It is the cancel-signal fast path so an aborted upload never starts network work.

Source

Thrown at project/aitoearn-web/src/api/materials/material.api.ts:111

  if (!confirmResponse || confirmResponse.code !== 0)
    throw new Error(confirmResponse?.message || '上传确认失败')

  return confirmResponse.data?.url || fallbackUrl
}

/**
 * 上传文件到OSS (前端直传 AWS S3)
 */
export async function uploadToOss(
  file: File | Blob,
  options?: UploadToOssOptions | ((prog: number) => void),
): Promise<string> {
  try {
    const opts: UploadToOssOptions
      = typeof options === 'function' ? { onProgress: options } : (options ?? {})

    if (opts.signal?.aborted) {
      throw new DOMException('上传已取消', 'AbortError')
    }

    const uploadFile = await optimizeImageForUpload(file, { signal: opts.signal })

    const fileName = getUploadFileName(uploadFile, opts.publicUploadId)

    const fileSize = uploadFile.size
    const contentType = uploadFile.type || 'application/octet-stream'

    // 获取 presigned post 数据
    const presignedData = await getPresignedPostData(fileName, fileSize, contentType, opts.publicUploadId)

    // R2 使用 PUT 请求直接上传到 uploadUrl,不需要 FormData
    const uploadUrl = presignedData.uploadUrl

    // 直传文件到 AWS S3 (支持进度回调)
    if (opts.onProgress) {
      return new Promise<string>((resolve, reject) => {

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Treat DOMException with name === 'AbortError' as an expected cancellation, not a failure — skip error toasts/logging.
  2. Create a fresh AbortController per upload attempt.
  3. If cancellation was unintentional (e.g. premature unmount), fix the effect cleanup so the controller isn't aborted too early.
  4. Check that the cancel UI only aborts the upload it owns.

Example fix

// before
try { await uploadToOss(file, { signal }) } catch (e) { showError(e) }
// after
try {
  await uploadToOss(file, { signal })
}
catch (e) {
  if (e instanceof DOMException && e.name === 'AbortError') return // cancelled
  showError(e)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before starting an upload
if (controller.signal.aborted) return // skip entirely; don't even call uploadToOss

Type guard

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

Try / catch

try {
  await uploadToOss(file, { signal: controller.signal })
}
catch (e) {
  if (isAbortError(e)) return // expected cancellation
  throw e
}

Prevention

When it happens

Trigger: The user/UI cancels an upload (abort() on the AbortController) before or as uploadToOss is invoked; a component unmount aborts the controller while the promise is being created; the same signal reused after a previous upload was cancelled.

Common situations: Users closing a modal/leaving a page mid-upload; React strict-mode double effects aborting the first attempt; reusing one AbortController across sequential uploads without resetting it.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/d964c82b1b792914. Report an issue: GitHub.