{"record":{"id":"5bbf3b90c060e697","repo":"yikart/AiToEarn","slug":"error-5bbf3b","errorCode":null,"errorMessage":"获取上传签名失败","messagePattern":"获取上传签名失败","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"project/aitoearn-web/src/api/materials/material.api.ts","lineNumber":79,"sourceCode":"  return ownerId\n    ? `${ownerId}/${hashedPrefix}${processedFileName}`\n    : `${hashedPrefix}${processedFileName}`\n}\n\n// 获取 R2 presigned post 数据\nasync function getPresignedPostData(fileName: string, fileSize: number, contentType: string, publicUploadId?: string) {\n  const res = await request<UploadSignData>({\n    url: getUploadRequestPath(publicUploadId),\n    method: 'POST',\n    data: {\n      filename: fileName,\n      size: fileSize,\n      type: getAssetType(fileName, contentType),\n    },\n  })\n\n  if (!res || res.code !== 0)\n    throw new Error(res?.message || '获取上传签名失败')\n\n  return res.data\n}\n\nasync function confirmUpload(assetId: string, fallbackUrl: string, publicUploadId?: string) {\n  const confirmResponse = await request<ConfirmUploadData>({\n    url: getConfirmRequestPath(assetId, publicUploadId),\n    method: 'POST',\n    data: {\n      id: assetId,\n    },\n  })\n\n  if (!confirmResponse || confirmResponse.code !== 0)\n    throw new Error(confirmResponse?.message || '上传确认失败')\n\n  return confirmResponse.data?.url || fallbackUrl\n}","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-web/src/api/materials/material.api.ts#L61-L97","documentation":" thrown by getPresignedPostData when the backend response for requesting an OSS/S3 presigned upload is missing or its code !== 0 (the app's success code). It prefers the server-provided res.message and falls back to the generic '获取上传签名失败'. Indicates the upload could not even start because no signed POST data was issued.","triggerScenarios":"Calling getPresignedPostData with fileName/fileSize/contentType when the backend fails: auth failure (401 wrapped in res.code), asset type not recognized, backend storage service misconfigured, or request returned undefined (network/HTTP error swallowed by the request layer).","commonSituations":"Expired login token; backend S3/OSS credentials invalid; getAssetType returning an unsupported type for the file extension; request interceptor returning undefined on network error.","solutions":["Log the full res object (code and message) — the server message usually names the exact cause.","Re-authenticate if the backend code indicates 401/invalid token.","Verify contentType/extension maps to a supported asset type in getAssetType.","Check backend presign service config (bucket/keys) if the message points to server-side failure."],"exampleFix":"// before\nconst res = await request<PresignedData>({ url: '/assets/presign', data })\nif (!res || res.code !== 0) throw new Error(res?.message || '获取上传签名失败')\n// after\nif (!res) throw new Error('网络错误，无法获取上传签名')\nif (res.code !== 0) throw new Error(`获取上传签名失败: ${res.message} (code=${res.code})`)","handlingStrategy":"try-catch","validationCode":"// before calling presign\nif (!file.name || file.size <= 0) throw new Error('文件无效')\nif (!isLoggedIn()) await refreshToken()","typeGuard":"function hasPresignedData(r: unknown): r is { code: number; message?: string; data: PresignedPostData } {\n  const o = r as any\n  return !!o && typeof o.code === 'number' && !!o.data && typeof o.data.url === 'string'\n}","tryCatchPattern":"try {\n  const presigned = await getPresignedPostData(fileName, fileSize, contentType)\n}\ncatch (e) {\n  showToast(e instanceof Error ? e.message : '获取上传签名失败')\n  return\n}","preventionTips":["Check login state before initiating uploads; refresh tokens proactively.","Support re-auth then retry when the presign code indicates auth failure.","Validate file extensions/contentTypes against supported asset types up front.","Log res.code and res.message server-side responses for every presign failure."],"tags":["upload","oss","presign","api"],"backgroundTag":"presigned-upload-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}