{"record":{"id":"5624c0ca53cc7f90","repo":"yikart/AiToEarn","slug":"responsecode-s3downloadfilefailed","errorCode":"ResponseCode.S3DownloadFileFailed","errorMessage":"S3DownloadFileFailed","messagePattern":"S3DownloadFileFailed","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/libs/ali-oss/src/ali-oss.service.ts","lineNumber":31,"sourceCode":"  ) {}\n\n  getClient(): OSS {\n    return this.client\n  }\n\n  async putObject(key: string, file: Buffer | string, options?: OSS.PutObjectOptions) {\n    return await this.client.put(key, file, options)\n  }\n\n  async putObjectFromUrl(url: string, objectPath: string) {\n    try {\n      await this.client.head(objectPath)\n      return { path: objectPath, exists: true }\n    }\n    catch {\n      const response = await fetch(url)\n      if (response.body === null) {\n        throw new AppException(ResponseCode.S3DownloadFileFailed)\n      }\n      const buffer = Buffer.from(await response.arrayBuffer())\n      const contentType = response.headers.get('content-type') || undefined\n      const options: OSS.PutObjectOptions = contentType ? { headers: { 'Content-Type': contentType } } : {}\n      await this.client.put(objectPath, buffer, options)\n      return { path: objectPath }\n    }\n  }\n\n  async getObject(key: string) {\n    return await this.client.get(key)\n  }\n\n  async deleteObject(key: string) {\n    return await this.client.delete(key)\n  }\n\n  async listObjects(query: OSS.ListObjectsQuery, options?: OSS.RequestOptions) {","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/libs/ali-oss/src/ali-oss.service.ts#L13-L49","documentation":"putObjectFromUrl downloads a remote file with fetch and uploads it to S3/OSS. If the object does not already exist (head failed) and the fetch returns a null body, the service throws AppException(ResponseCode.S3DownloadFileFailed).","triggerScenarios":"fetch(url) succeeds at HTTP level (or fails silently to produce a body) but response.body is null — e.g. 204/304 responses, redirects to empty bodies, or unreachable/blocked URLs resolved by the fetch polyfill.","commonSituations":"Source URL returns 404/410 but fetch config yields a null body, the remote host blocks the server's egress IP, DNS failure in a container, or an expired signed URL.","solutions":["Log response.status before throwing; if it is 4xx the source URL is bad — fix or refresh the URL","Retry with a fresh signed URL if the original expired","Check egress network/DNS from the deployment environment (curl the URL from inside the container)","Optionally check response.ok and include status text in a richer error"],"exampleFix":"// before\nconst response = await fetch(url)\nif (response.body === null) throw new AppException(ResponseCode.S3DownloadFileFailed)\n// after\nconst response = await fetch(url)\nif (!response.ok || response.body === null) {\n  throw new AppException(ResponseCode.S3DownloadFileFailed, `status=${response.status}`)\n}","handlingStrategy":"retry","validationCode":"const probe = await fetch(url, { method: 'HEAD' })\nif (!probe.ok) throw new Error(`Source URL unreachable before upload: ${probe.status}`)","typeGuard":"function isDownloadable(res: Response): boolean {\n  return res.ok && res.body !== null\n}","tryCatchPattern":"try {\n  await ossService.putObjectFromUrl(url, objectPath)\n} catch (e) {\n  if (e.code === ResponseCode.S3DownloadFileFailed) {\n    await withBackoff(() => ossService.putObjectFromUrl(freshUrl(url), objectPath), 3)\n  } else throw e\n}","preventionTips":["Pre-flight HEAD-check source URLs before queuing downloads","Use fresh, unexpired signed URLs","Verify egress network/DNS from the deployment environment","Include HTTP status in logs/error messages when downloads fail"],"tags":["s3","oss","network","download","fetch"],"backgroundTag":"s3-download-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}