mastra-ai/mastra · error · Error
Failed to complete upload
Error message
Failed to complete upload: ${(err as { detail?: string }).detail || completeResp.statusText} What it means
The final step of uploadToEnvironment signals the platform that the artifact upload finished (upload-complete endpoint). A non-ok response throws with the server's `detail` when available, else statusText. The artifact bytes may already be stored, but the deploy was not finalized, so the platform may keep the deploy in an incomplete state.
Solutions
- Read the `detail` in the message for the server's exact reason
- Re-run the deploy end-to-end (a fresh create/upload cycle) rather than retrying only upload-complete
- Check token validity/auth duration for long CI jobs and refresh or extend it
- Confirm no other deploy pipeline is concurrently targeting the same environment
Example fix
// before // 40-min CI job, token expires before upload-complete -> 401 // after // refresh token before deploy step (or use a token with sufficient expiry), re-run deploy
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the token won't expire mid-deploy for long CI jobs
const jobDurationMs = 40 * 60 * 1000;
const tokenTtlMs = getTokenTtl(token); // decode exp from token
if (tokenTtlMs < jobDurationMs) throw new Error('Token expires before deploy can finish; refresh it'); Type guard
function isCompleteUploadFailure(err: unknown): err is Error {
return err instanceof Error && err.message.startsWith('Failed to complete upload:');
} Try / catch
try {
const deploy = await uploadToEnvironment(args);
} catch (err) {
if (isCompleteUploadFailure(err)) {
console.error('Deploy not finalized:', err.message);
// re-run the full deploy (create + upload + complete), not just upload-complete
} else throw err;
} Prevention
- Use tokens with TTL exceeding the whole CI job duration
- Re-run the entire deploy flow after a finalize failure — don't call upload-complete standalone
- Avoid concurrent deploy pipelines on the same environment
- Log the `detail` field for platform-side support when it persists
When it happens
Trigger: The POST to /v1/projects/{projectId}/environments/{environmentId}/deploys/{deploy.id}/upload-complete returns non-2xx — e.g., server-side verification of the uploaded artifact failed, auth expired between steps, or the deploy record was cancelled/unknown.
Common situations: Token expiry mid-deploy in long CI runs; upload-complete racing server-side artifact verification; concurrent deploy superseding this one; platform-side validation rejecting an unexpected artifact layout.
Related errors
- Failed to create deploy
- Course state update failed with status
- detail
- Failed to create access token
- Failed to create project
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/19860302ede42a1b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/commands/deploy/index.ts:398
throw new Error(`Failed to upload artifact: ${uploadResp.statusText}`);
}
// Signal upload complete — uses net-new env-scoped endpoint so the
// unified-runtime CLI never touches /v1/studio/*.
const completeResp = await fetch(
`${apiUrl}/v1/projects/${projectId}/environments/${environmentId}/deploys/${deploy.id}/upload-complete`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'x-organization-id': orgId,
},
},
);
if (!completeResp.ok) {
const err = await completeResp.json().catch(() => ({}));
throw new Error(`Failed to complete upload: ${(err as { detail?: string }).detail || completeResp.statusText}`);
}
return deploy;
}
interface UnifiedDeployStatus {
id: string;
status: string;
instanceUrl: string | null;
error: string | null;
}
/**
* Poll the net-new env-scoped status endpoint until the deploy reaches a
* terminal state. Kept inside the deploy command so the unified runtime
* never reaches into ../studio/ for transport.
*/
async function streamEnvironmentDeployLogs(View on GitHub (pinned to 75dd419e61)