Skip to content

fix: timeout stuck video creation - #1876

Closed
steebchen wants to merge 1 commit into
mainfrom
video-create-timeout
Closed

steebchen wants to merge 1 commit into
mainfrom
video-create-timeout

Conversation

@steebchen

@steebchen steebchen commented Mar 23, 2026 •

Copy link
Copy Markdown
Member

Summary

Add explicit timeouts to video creation so stuck upstream provider requests fail with a clear 504 instead of hanging indefinitely.
Apply the same fail-fast behavior in the playground /api/video proxy so the UI gets JSON before the route times out, and preserve provider fallback when a create call times out.
Add mock-server coverage and gateway tests for timed-out video creation and timeout-driven fallback.

Verification

pnpm build

Summary by CodeRabbit

  • Improvements

    • Implemented timeout protection for video creation requests; when creation exceeds the configured timeout, the system returns an error and attempts fallback to alternative providers when available.
  • Tests

    • Added test coverage for video creation timeout scenarios and provider fallback behavior.

Copilot AI review requested due to automatic review settings March 23, 2026 19:02
@coderabbitai

coderabbitai Bot commented Mar 23, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

This pull request introduces video creation timeout handling throughout the gateway and playground services. It adds timeout configuration functions, implements upstream timeout detection with fallback to alternate providers when video creation requests exceed configured time limits, and includes mock server support for testing provider-specific timeout scenarios.

Changes

Cohort / File(s) Summary
Video Create Timeout Configuration
apps/gateway/src/lib/timeout-config.ts
Added getVideoCreateTimeoutMs() and createVideoCreateTimeoutSignal() functions to compute and manage upstream video creation timeouts with configurable environment variable fallback.
Upstream Video Creation Error Handling
apps/gateway/src/videos/videos.ts
Modified fetchUpstreamJson to detect and handle video creation timeouts via dedicated abort signal and try/catch block; returns HTTP 504 with timeout message on timeout errors.
Playground Gateway Proxy Timeout
apps/playground/src/app/api/video/route.ts
Added helper functions for computing proxy timeout and detecting timeout errors; wrapped gateway fetch call with AbortSignal.timeout() and timeout-specific error handling returning HTTP 504.
Mock Server Provider-Specific Timeouts
apps/gateway/src/test-utils/mock-openai-server.ts
Added extractProviderSpecificTimeoutDelay() to parse provider-specific timeout triggers and updated video, Veo, and Vertex mock endpoints to delay responses based on extracted timeout directives.
Video Creation Timeout Tests
apps/gateway/src/videos/videos.spec.ts
Added two test cases: one verifying HTTP 504 response and timeout logging when video create request exceeds AI_VIDEO_CREATE_TIMEOUT_MS, and another validating fallback to next provider (avalanche) when first provider (google-vertex) times out, with routing metadata recording both attempts.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Gateway
    participant Provider1 as Provider 1<br/>(google-vertex)
    participant Provider2 as Provider 2<br/>(avalanche)

    Client->>Gateway: POST /v1/videos<br/>(video create request)
    Gateway->>Provider1: Create video request<br/>(with timeout signal)
    Provider1-->>Gateway: Timeout (exceeds<br/>AI_VIDEO_CREATE_TIMEOUT_MS)
    Gateway->>Gateway: Detect timeout error<br/>Log warning + 504 candidate
    Gateway->>Provider2: Retry: Create video request<br/>(next provider in eligibility)
    Provider2->>Provider2: Process video creation
    Provider2-->>Gateway: 200 OK + video ID
    Gateway->>Gateway: Record routing metadata:<br/>Provider1 (504/upstream_error)<br/>Provider2 (200/none)
    Gateway-->>Client: 200 OK<br/>(video job persisted)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: timeout stuck video creation' directly describes the main change: adding explicit timeouts to video creation to prevent requests from hanging indefinitely.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch video-create-timeout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds explicit, configurable timeouts around upstream video creation so requests don’t hang indefinitely, and ensures both the gateway and playground proxy return a clear 504 on timeout while preserving provider fallback behavior.

Changes:

  • Add video-create-specific timeout configuration helpers and apply them to upstream fetches in the gateway.
  • Add a timeout + 504 JSON response to the playground /api/video proxy route.
  • Extend mock server triggers and add gateway tests covering timeout errors and timeout-driven fallback.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
apps/playground/src/app/api/video/route.ts Adds an abort timeout to the playground proxy so the UI gets a JSON 504 instead of hanging.
apps/gateway/src/videos/videos.ts Wraps upstream video fetches with a video-create timeout signal and returns HTTP 504 on timeout.
apps/gateway/src/videos/videos.spec.ts Adds test coverage for upstream create timeouts and fallback after a timeout.
apps/gateway/src/test-utils/mock-openai-server.ts Adds provider-specific timeout delay triggers to simulate stuck upstreams in tests.
apps/gateway/src/lib/timeout-config.ts Introduces AI_VIDEO_CREATE_TIMEOUT_MS-backed timeout getter + AbortSignal factory for video creation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

: await c.req.json();
const prompt = typeof body.prompt === "string" ? body.prompt : "";
const timeoutDelay = extractProviderSpecificTimeoutDelay(prompt, "obsidian");
if (timeoutDelay) {

Copilot AI Mar 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractProviderSpecificTimeoutDelay can legitimately return 0 (e.g., ..._0), but the current if (timeoutDelay) check treats 0 as falsy and skips the delay. Use an explicit null check (e.g., timeoutDelay !== null) so the behavior matches the trigger value exactly.

Suggested change
if (timeoutDelay) {
if (timeoutDelay !== null) {

Copilot uses AI. Check for mistakes.
const body = await c.req.json();
const prompt = typeof body.prompt === "string" ? body.prompt : "";
const timeoutDelay = extractProviderSpecificTimeoutDelay(prompt, "avalanche");
if (timeoutDelay) {

Copilot AI Mar 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractProviderSpecificTimeoutDelay can return 0, but if (timeoutDelay) treats it as falsy and won’t apply the delay. Prefer checking timeoutDelay !== null (or typeof timeoutDelay === 'number') before calling delay(...).

Suggested change
if (timeoutDelay) {
if (typeof timeoutDelay === "number") {

Copilot uses AI. Check for mistakes.
prompt,
"vertex",
);
if (timeoutDelay) {

Copilot AI Mar 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractProviderSpecificTimeoutDelay returns number | null; using if (timeoutDelay) skips the delay for 0. Switch to an explicit null check so ..._0 behaves as intended and to avoid relying on truthiness.

Suggested change
if (timeoutDelay) {
if (timeoutDelay !== null) {

Copilot uses AI. Check for mistakes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: caef6dafce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1978 to +1982
logger.warn("Upstream video create request timed out", {
url,
timeoutMs: getVideoCreateTimeoutMs(),
});
throw new HTTPException(504, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid falling back after a timed-out create POST

The new 504 here is treated as retryable by the outer loop in apps/gateway/src/videos/videos.ts:2811-2844, so a timeout on provider A now immediately sends the same create request to provider B. That is unsafe for these side-effecting POSTs: createObsidianVideoJob and createAvalancheVideoJob build plain create payloads with no idempotency key or cancellation handle, so a slow provider can still enqueue the first job after our client gives up. In that case one user request can create two upstream videos (and two billable jobs), while we only persist the fallback attempt.

Useful? React with 👍 / 👎.

Comment on lines +57 to +58
body: JSON.stringify(requestBody),
signal: AbortSignal.timeout(getVideoCreateProxyTimeoutMs()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate the proxy timeout to the gateway request

This new timeout makes the playground return a 504 after ~55s, but the gateway video-create path never threads the incoming request abort signal into its upstream work (I checked apps/gateway/src/videos/videos.ts and the create helpers only use their own timeout signal). For requests that spend a long time preprocessing/uploading images or timing out on one provider before falling back, the UI can now report failure while /v1/videos keeps running and eventually inserts a videoJob; if the user retries after that JSON error, they can duplicate the generation and billing.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/playground/src/app/api/video/route.ts (1)

10-17: Add parentheses to clarify operator precedence.

ESLint flags the mixed * and - operators. While the current evaluation order is correct, adding explicit parentheses improves readability.

Suggested fix
 function getVideoCreateProxyTimeoutMs(): number {
 	const envValue = Number(process.env.PLAYGROUND_VIDEO_CREATE_TIMEOUT_MS);
 	if (envValue > 0) {
 		return envValue;
 	}

-	return Math.max(1000, maxDuration * 1000 - 5000);
+	return Math.max(1000, (maxDuration * 1000) - 5000);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/playground/src/app/api/video/route.ts` around lines 10 - 17, The
expression in getVideoCreateProxyTimeoutMs uses mixed * and - operators which
ESLint flags; update the fallback return to use explicit parentheses so
precedence is clear — change Math.max(1000, maxDuration * 1000 - 5000) to
Math.max(1000, (maxDuration * 1000) - 5000) (referring to
getVideoCreateProxyTimeoutMs, PLAYGROUND_VIDEO_CREATE_TIMEOUT_MS and
maxDuration).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/playground/src/app/api/video/route.ts`:
- Around line 10-17: The expression in getVideoCreateProxyTimeoutMs uses mixed *
and - operators which ESLint flags; update the fallback return to use explicit
parentheses so precedence is clear — change Math.max(1000, maxDuration * 1000 -
5000) to Math.max(1000, (maxDuration * 1000) - 5000) (referring to
getVideoCreateProxyTimeoutMs, PLAYGROUND_VIDEO_CREATE_TIMEOUT_MS and
maxDuration).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 39703491-3273-433c-b39a-41e8aeffb328

📥 Commits

Reviewing files that changed from the base of the PR and between 0a9fdab and caef6da.

📒 Files selected for processing (5)
  • apps/gateway/src/lib/timeout-config.ts
  • apps/gateway/src/test-utils/mock-openai-server.ts
  • apps/gateway/src/videos/videos.spec.ts
  • apps/gateway/src/videos/videos.ts
  • apps/playground/src/app/api/video/route.ts

Copy link
Copy Markdown
Member Author

Closing this duplicate in favor of #1882, which contains the same video timeout work rebased onto the latest main.

@steebchen steebchen closed this Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants