yikart/AiToEarn · error · AppException
ResponseCode.S3DownloadFileFailed
ResponseCode.S3DownloadFileFailed
Error message
S3DownloadFileFailed
What it means
S3DownloadFileFailed is thrown by S3Service.putObjectFromUrl when it fetches the source URL and response.body is null, i.e. there is no readable body to stream into object storage. The service first checks if the object already exists; if not, it downloads from the URL and re-uploads it — a null body aborts that.
Source
Thrown at project/aitoearn-backend/libs/aws-s3/src/s3.service.ts:73
const command = new HeadObjectCommand({
Bucket: this.config.bucketName,
Key: objectPath,
})
return await this.client.send(command)
}
async putObjectFromUrl(
url: string,
objectPath: string,
) {
try {
await this.headObject(objectPath)
return { path: objectPath, exists: true }
}
catch {
const response = await fetch(url)
if (response.body === null) {
throw new AppException(ResponseCode.S3DownloadFileFailed)
}
const contentType = response.headers.get('content-type') || undefined
return this.putObject(objectPath, response.body, contentType)
}
}
// 生成预签名上传 URL
async getUploadSignPost(objectPath: string, contentType?: string) {
const result = await createPresignedPost(this.signingClient, {
Bucket: this.config.bucketName,
Key: objectPath,
Expires: this.config.signExpires,
Conditions: contentType ? [['eq', '$Content-Type', contentType]] : undefined,
Fields: contentType ? { 'Content-Type': contentType } : undefined,
})
return result
}
View on GitHub (pinned to d3aa8bea5b)
Solutions
- Verify the source URL is reachable and returns a 200 with a body (curl -I)
- Re-generate or refresh signed/expired source URLs
- Add retry with backoff around putObjectFromUrl for transient network failures
- Check the response status code before calling the endpoint and fail fast with a clearer error
Example fix
// before
await s3Service.putObjectFromUrl(url, path) // url expired
// after
const head = await fetch(url, { method: 'HEAD' })
if (!head.ok) throw new Error(`source unavailable: ${head.status}`)
await s3Service.putObjectFromUrl(url, path) Defensive patterns
Strategy: retry
Validate before calling
const probe = await fetch(url, { method: 'HEAD' })
if (!probe.ok) throw new Error(`source URL not downloadable: ${probe.status}`) Try / catch
try {
await s3Service.putObjectFromUrl(url, path)
} catch (e) {
if (e instanceof AppException && e.code === ResponseCode.S3DownloadFileFailed) {
// retry with backoff or mark import as failed with source-URL context
} else throw e
} Prevention
- Verify source URLs (200 + body) before import
- Refresh signed URLs before use; track expiry
- Wrap remote imports in retry with exponential backoff
- Log the source URL with the failure for diagnosis
When it happens
Trigger: The remote URL returns an empty/204 body, a redirect to an empty response, a network-layer failure surfacing as a body-less response, or the URL points at a resource that no longer exists (some servers return null body with error statuses that fetch doesn't throw on depending on setup).
Common situations: Importing an asset from a third-party URL that now 404s or redirects; expired signed URL for the source file; hotlink protection returning empty responses; timeout between fetch and body read.
Related errors
- S3DownloadFileFailed
- ResponseCode.S3DownloadFileFailed
- Failed to fetch image: ${response.status} ${response.statusT
- Download failed: ${response.status}
- Relay uploadSign returned no uploadUrl: ${JSON.stringify(sig
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/5791c6fbb4e4dc23.
Report an issue: GitHub.