mastra-ai/mastra · critical · Error

No upload URL returned

Error message

No upload URL returned

What it means

uploadDeploy first POSTs to /v1/studio/deploys to create a deploy record and expects the response's deploy object to include a presigned uploadUrl. If the API responds successfully (2xx) but omits uploadUrl, the client cannot upload the zip and throws instead of failing obscurely later.

Source

Thrown at packages/cli/src/commands/studio/platform-api.ts:167

        'x-mastra-version': meta?.mastraVersion,
      },
    },
    body: {
      envVars: meta?.envVars,
      ...(meta?.disablePlatformObservability !== undefined
        ? { disablePlatformObservability: meta.disablePlatformObservability }
        : {}),
    },
  });

  if (error) {
    throwApiError('Deploy failed', response.status, error.detail);
  }

  const { id, status, uploadUrl } = data.deploy;

  if (!uploadUrl) {
    throw new Error('No upload URL returned');
  }

  const cancel = (c: ReturnType<typeof createApiClient>) =>
    bestEffortCancel({
      postCancel: c2 => c2.POST('/v1/studio/deploys/{id}/cancel', { params: { path: { id } } }),
      client: c,
      deployId: id,
    });

  // Step 2: Upload artifact to the signed URL
  try {
    if (uploadUrl.startsWith('file://')) {
      const { writeFile } = await import('node:fs/promises');
      const { fileURLToPath } = await import('node:url');
      await writeFile(fileURLToPath(uploadUrl), Buffer.from(zipBuffer));
    } else {
      const uploadResp = await fetch(uploadUrl, {
        method: 'PUT',

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Update @mastra/cli (and core packages) to the latest version so the API contract matches
  2. Retry the deploy — transient server issues can omit the upload URL
  3. Check Mastra status/changelog for known API incidents
  4. If persisting, report the issue with the deploy id and response captured via debug logging

Example fix

// before
pnpm dlx mastra@old studio deploy --yes
// Error: No upload URL returned
// after
pnpm dlx mastra@latest studio deploy --yes
Defensive patterns

Strategy: retry

Validate before calling

const { data } = await client.POST('/v1/studio/deploys', { body });
const deploy = data?.deploy as { id: string; uploadUrl?: string } | undefined;
if (!deploy?.uploadUrl) {
  throw new Error(`API returned no uploadUrl for deploy ${deploy?.id ?? '?'}`);
}

Type guard

function hasUploadUrl(d: { uploadUrl?: unknown }): d is { uploadUrl: string } {
  return typeof d.uploadUrl === 'string' && d.uploadUrl.length > 0;
}

Try / catch

try {
  await uploadDeploy(zipBuffer);
} catch (e) {
  if (e instanceof Error && e.message === 'No upload URL returned') {
    console.error('Server did not provide an upload URL; update CLI and retry once.');
    process.exitCode = 3;
  } else throw e;
}

Prevention

When it happens

Trigger: The Studio API returns a deploy payload without uploadUrl — typically a server-side/contract change, an unexpected API version mismatch between CLI and server, or the deploy record being created in a state that skips URL generation.

Common situations: Running an outdated CLI against a newer API (or vice versa); server incident where deploys are created but upload signing fails silently; proxy/gateway stripping or rewriting response bodies.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/d9ccd8689e4577f7. Report an issue: GitHub.