iflytek/astron-agent · error · Error

上传失败

Error message

上传失败: ${status} ${statusText}

What it means

uploadFileToS3 wraps its upload request; if the axios PUT/POST to S3 (or the presigned-URL fetch) fails, it rethrows a generic Error '上传失败: <status> <statusText>' with the HTTP status. This is an HTTP-level failure of the upload request, not an application-level error code.

Solutions

  1. Log the full error (error.response?.data) — S3 error bodies contain the real reason (SignatureDoesNotMatch, AccessDenied, EntityTooLarge).
  2. Fetch the presigned URL immediately before upload and upload within its TTL; regenerate on failure and retry once.
  3. Check S3/MinIO CORS configuration to allow the console origin, PUT method, and the content-type header.
  4. Surface a user-visible toast with the status and a retry button instead of only throwing.

Example fix

// before
catch (error: any) {
  const status = error.response?.status || 500;
  throw new Error(`上传失败: ${status} ${statusText}`);
}
// after
catch (error: any) {
  if (error.code === 'ERR_NETWORK') {
    throw new Error('上传失败: 网络错误或CORS被拦截');
  }
  const s3Reason = error.response?.data ? String(error.response.data).slice(0, 200) : '';
  const status = error.response?.status || 500;
  throw new Error(`上传失败: ${status} ${s3Reason || error.message}`);
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight checks before upload
if (file.size > MAX_UPLOAD_BYTES) throw new Error('文件过大');
if (Date.now() > presignExpiresAt) presign = await fetchPresignedUrl(file);

Type guard

function isRetryableUploadError(e: any): boolean {
  const s = e?.response?.status;
  return !s || s === 408 || s === 429 || s >= 500;
}

Try / catch

try {
  await uploadFileToS3(url, file);
} catch (e: any) {
  if (isRetryableUploadError(e)) {
    await retryUpload(file, 2);
  } else {
    message.error(`上传失败: ${e.message}`);
  }
}

Prevention

When it happens

Trigger: The S3/MinIO presigned URL has expired before upload; CORS blocks the PUT from the browser (error.response undefined → status 500 with axios message); file exceeds bucket size limits or the configured content-type doesn't match the signature; network failure or 403 from wrong credentials.

Common situations: Slow uploads of large files exceeding presigned-URL TTL; MinIO/S3 bucket policy or CORS misconfiguration in self-hosted docker deployments; clock skew invalidating SigV4 signatures; uploading disallowed file types.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/190f5088239e0462. Report an issue: GitHub.

Appendix: source

Thrown at console/frontend/src/services/chat.ts:207

      };
    }

    // 设置取消令牌
    if (abortController) {
      config.signal = abortController.signal;
    }
    // 使用axios发送PUT请求,ArrayBuffer作为data参数
    const axiosResponse = await axios.create().put(url, data, config);

    // 将axios响应转换为Response对象以保持接口一致性
    return new Response(null, {
      status: axiosResponse.status,
      statusText: axiosResponse.statusText,
    });
  } catch (error: any) {
    const status = error.response?.status || 500;
    const statusText = error.response?.statusText || error.message;
    throw new Error(`上传失败: ${status} ${statusText}`);
  }
};

/**
 * 上传文件绑定对话
 * @param params
 * @param signal 可选的 AbortSignal,用于取消请求
 * @returns
 */
export const uploadFileBindChat = (
  params: {
    chatId: number;
    fileSize: number;
    fileName: string;
    fileUrl: string;
    fileBusinessKey: string;
    paramName?: string;
  },

View on GitHub (pinned to 5e758547a8)