{"record":{"id":"f9e8894d40dfcf24","repo":"mastra-ai/mastra","slug":"failed-to-upload-artifact-uploadresp-statustext","errorCode":null,"errorMessage":"Failed to upload artifact: ${uploadResp.statusText}","messagePattern":"Failed to upload artifact: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/commands/deploy/index.ts","lineNumber":380,"sourceCode":"\n  if (!createResp.ok) {\n    const err = await createResp.json().catch(() => ({}));\n    throw new Error(`Failed to create deploy: ${(err as { detail?: string }).detail || createResp.statusText}`);\n  }\n\n  const { deploy } = (await createResp.json()) as { deploy: { id: string; uploadUrl: string } };\n\n  // Upload artifact\n  const uploadResp = await fetch(deploy.uploadUrl, {\n    method: 'PUT',\n    headers: {\n      'Content-Type': 'application/zip',\n    },\n    body: zipBuffer,\n  });\n\n  if (!uploadResp.ok) {\n    throw new Error(`Failed to upload artifact: ${uploadResp.statusText}`);\n  }\n\n  // Signal upload complete — uses net-new env-scoped endpoint so the\n  // unified-runtime CLI never touches /v1/studio/*.\n  const completeResp = await fetch(\n    `${apiUrl}/v1/projects/${projectId}/environments/${environmentId}/deploys/${deploy.id}/upload-complete`,\n    {\n      method: 'POST',\n      headers: {\n        Authorization: `Bearer ${token}`,\n        'x-organization-id': orgId,\n      },\n    },\n  );\n\n  if (!completeResp.ok) {\n    const err = await completeResp.json().catch(() => ({}));\n    throw new Error(`Failed to complete upload: ${(err as { detail?: string }).detail || completeResp.statusText}`);","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/cli/src/commands/deploy/index.ts#L362-L398","documentation":"After creating the deploy record, uploadToEnvironment PUTs/POSTs the zip artifact to the returned uploadUrl. A non-ok response here throws with only the HTTP statusText (this endpoint is a raw upload sink that typically returns no JSON body). It means the storage upload itself failed — network, credentials, or an expired presigned URL.","triggerScenarios":"The artifact upload fetch to deploy.uploadUrl returns a non-2xx status — e.g., 403 on an expired signed URL, 413 for an oversized zip, 5xx from storage, or an interrupted/unreachable connection reported via statusText.","commonSituations":"Slow CI networks or large bundles causing storage timeouts; zip exceeding upload size limits; long delay between deploy creation and upload letting the presigned URL lapse; proxy/firewall blocking the storage host.","solutions":["Retry the deploy — presigned-URL or transient storage failures usually clear on retry","Reduce artifact size (exclude node_modules/dev assets from the zip) if statusText suggests a size limit (413)","Deploy again promptly after deploy creation so the signed upload URL doesn't expire","Check network/proxy egress to the storage host from your CI environment"],"exampleFix":"// before\n// zip includes node_modules -> 413 Payload Too Large\n// after\n// build step: zip only dist/ + package.json, then retry deploy","handlingStrategy":"retry","validationCode":"// Check artifact size before upload (guard against 413)\nconst MAX_ZIP_BYTES = 100 * 1024 * 1024;\nif (zipBuffer.byteLength > MAX_ZIP_BYTES) {\n  throw new Error('Artifact ' + zipBuffer.byteLength + ' bytes exceeds upload limit');\n}","typeGuard":"function isUploadFailure(err: unknown): err is Error {\n  return err instanceof Error && err.message.startsWith('Failed to upload artifact:');\n}","tryCatchPattern":"try {\n  const deploy = await uploadToEnvironment(args);\n} catch (err) {\n  if (isUploadFailure(err) && isRetryableStatus(err.message)) {\n    await uploadToEnvironment(args); // one retry for transient storage/network errors\n  } else throw err;\n}","preventionTips":["Keep artifacts small: zip only dist/ and manifests, never node_modules","Upload immediately after deploy creation so the signed URL doesn't expire","Ensure CI egress allows the storage upload host","Retry once on transient 5xx/timeouts before failing the pipeline"],"tags":["upload","network","http","artifact"],"backgroundTag":"artifact-upload-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}