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
- Log the full error (error.response?.data) — S3 error bodies contain the real reason (SignatureDoesNotMatch, AccessDenied, EntityTooLarge).
- Fetch the presigned URL immediately before upload and upload within its TTL; regenerate on failure and retry once.
- Check S3/MinIO CORS configuration to allow the console origin, PUT method, and the content-type header.
- 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
- Fetch presigned URLs immediately before upload and regenerate expired ones
- Configure S3/MinIO CORS for the console origin, PUT, and content-type headers
- Enforce client-side file size/type limits matching server policy
- Keep server clocks in sync (NTP) to avoid signature mismatches
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)