musistudio/claude-code-router · error · Error
Remote media result has no URL.
Error message
Remote media result has no URL.
What it means
Thrown by MediaService's download executor when the media execution result has no remoteUrl to fetch. The downloader needs the provider's artifact URL; a result without one cannot be downloaded locally.
Source
Thrown at packages/core/src/media/executors.ts:100
await delay(2000, undefined, { signal });
continue;
}
const status = readString(payload, "status")?.toLowerCase();
if (status === "done" || status === "completed" || status === "succeeded") {
const url = readNestedString(payload, ["video", "url"]) ?? readString(payload, "url");
if (!url) throw mediaError("invalid_api_response", `${this.target.providerName} video API completed without an artifact URL.`, false);
return { fileName: `${requestId}.mp4`, remoteUrl: url, usage: readUsage(payload) };
}
if (status === "failed" || status === "expired" || status === "canceled" || status === "cancelled") {
const message = readNestedString(payload, ["error", "message"]) ?? readString(payload, "message") ?? `Video generation ${status}.`;
throw mediaError(`video_${status}`, message, status === "failed");
}
await delay(2000, undefined, { signal });
}
}
async download(result: MediaExecutionResult, signal: AbortSignal): Promise<MediaExecutionResult> {
if (!result.remoteUrl) throw new Error("Remote media result has no URL.");
let response: Response | undefined;
let lastError: unknown;
for (let attempt = 1; attempt <= 3; attempt += 1) {
try {
const candidate = await fetchMediaArtifact(result.remoteUrl, this.target.providerBaseUrl, signal);
if (candidate.ok || (candidate.status < 500 && candidate.status !== 408 && candidate.status !== 429)) {
response = candidate;
break;
}
lastError = mediaError("artifact_download_failed", `Failed to download generated artifact: HTTP ${candidate.status}.`, true);
await candidate.body?.cancel();
} catch (error) {
if (isExplicitlyNonRetryableError(error)) throw error;
lastError = error;
}
if (attempt < 3) await delay(attempt * 500, undefined, { signal });
}
if (!response) throw lastError ?? mediaError("artifact_download_failed", "Failed to download generated artifact.", true);View on GitHub (pinned to 99f24806c6)
Solutions
- Check result.remoteUrl before calling download; skip or handle local results separately
- If the provider should have returned a URL, inspect the raw provider response — a schema/API change may require a library update
- For tests, construct results with a remoteUrl pointing at a fixture server
Example fix
// before
const out = await media.download(result, signal);
// after
if (!result.remoteUrl) {
throw new Error(`Cannot download job ${result.id}: no remote URL`);
}
const out = await media.download(result, signal); Defensive patterns
Strategy: type-guard
Type guard
const hasRemoteUrl = (r: MediaExecutionResult): r is MediaExecutionResult & { remoteUrl: string } =>
typeof r.remoteUrl === "string" && r.remoteUrl.length > 0; Prevention
- Guard remoteUrl before download; skip local-only results
- Update the library when providers change response schemas
When it happens
Trigger: Calling download(result, signal) on a MediaExecutionResult whose remoteUrl is missing/empty — e.g. a result synthesized locally, an upload-only flow, or a provider response that returned no URL field.
Common situations: Passing a local-generation result to the downloader; provider schema change dropping the URL field; a mock/stub result in tests; job retried after a partial provider response.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Media model is not configured by a provider: ${normalizedSel
- Provider ${provider.name} does not declare ${kind ?? "media"
- Provider ${provider.name} does not configure a media API bas
- Claude App profiles do not support agent arguments.
- No Bot Gateway conversationRef is available for media respon
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/9ac718c4a96c8929.
Report an issue: GitHub.