n8n-io/n8n · error · NodeOperationError

Failed to retrieve download URL for file ${fileId}

Error message

Failed to retrieve download URL for file ${fileId}

What it means

Thrown by getVideoDownloadUrl when GET /files/retrieve returns a response whose response.file.download_url is missing. After polling confirms Success and yields a file_id, this second call fetches the signed download URL; absence of download_url means the node cannot retrieve the rendered video bytes.

Source

Thrown at packages/@n8n/nodes-langchain/nodes/vendors/MiniMax/transport/index.ts:98

	}

	throw new NodeOperationError(
		this.getNode(),
		`Video task ${taskId} did not complete within the maximum polling time. You can query the task manually using the task ID.`,
	);
}

export async function getVideoDownloadUrl(
	this: IExecuteFunctions,
	fileId: string,
): Promise<string> {
	const response = await apiRequest.call(this, 'GET', '/files/retrieve', {
		qs: { file_id: fileId },
	});

	const downloadUrl = response?.file?.download_url as string;
	if (!downloadUrl) {
		throw new NodeOperationError(
			this.getNode(),
			`Failed to retrieve download URL for file ${fileId}`,
		);
	}

	return downloadUrl;
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Retry GET /files/retrieve?file_id=<id> after a short delay to let the file materialize.
  2. Log the full response to confirm download_url is absent vs. relocated.
  3. If the file_id is old, resubmit the video generation — the file may have been garbage-collected.
  4. Verify the credentials base URL is the official MiniMax endpoint.
Defensive patterns

Strategy: retry

Validate before calling

// No caller-side input prevents an upstream missing-download_url response.
// Validate only that the fileId you pass is a non-empty string.
function validateFileId(fileId: unknown): string | null {
  if (typeof fileId !== 'string' || fileId.length === 0) return 'fileId must be a non-empty string';
  return null;
}

Type guard

function hasDownloadUrl(r: unknown): r is { file: { download_url: string } } {
  const url = (r as { file?: { download_url?: unknown } })?.file?.download_url;
  return typeof url === 'string' && url.length > 0;
}

Try / catch

async function getDownloadUrlWithRetry(apiRequest: any, fileId: string, maxAttempts = 3): Promise<string> {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const response = await apiRequest.call(this, 'GET', '/files/retrieve', { qs: { file_id: fileId } });
    const url = response?.file?.download_url as string | undefined;
    if (url) return url;
    await sleep(2000 * (attempt + 1)); // let the file materialize
  }
  throw new NodeOperationError(this.getNode(), `Failed to retrieve download URL for file ${fileId}`);
}

Prevention

When it happens

Trigger: File not yet materialized in MiniMax's storage when /files/retrieve was called (race after Success); file TTL expired between job completion and retrieval call; permission/quota issue preventing download URL minting; API revision moving download_url to a different field; proxy stripping the field.

Common situations: Tight race between Success and file availability; long delay between poll completion and retrieval (file GC'd); account download permission revoked; non-official base URL filtering the response.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/c608d5f7b34f24a5. Report an issue: GitHub.