sim icon indicating copy to clipboard operation
sim copied to clipboard

fix(http): resolve 5-minute timeout limit for API block requests

Open majiayu000 opened this issue 1 month ago • 2 comments

Summary

This PR fixes the root cause of issue #2242 where API block requests timeout at 5 minutes regardless of user-configured timeout settings.

Root Cause

Bun/Node.js fetch has a hardcoded 5-minute (300 seconds) default timeout that cannot be overridden by AbortSignal.timeout(). This is documented in Bun PR #6217.

Solution

  • Add timeout: false to all fetch calls to disable Bun/Node.js default 5-minute timeout
  • Use AbortSignal.timeout() for user-configurable timeout control (default: 2 min, max: 10 min)
  • Add maxDuration = 600 to /api/proxy and /api/workflows/[id]/execute routes for Next.js

Changes

File Changes
apps/sim/tools/index.ts Add parseTimeout() function, add timeout: false + AbortSignal.timeout() to handleInternalRequest and handleProxyRequest
apps/sim/app/api/proxy/route.ts Add maxDuration = 600 and timeout: false
apps/sim/app/api/workflows/[id]/execute/route.ts Add maxDuration = 600
apps/sim/tools/http/types.ts Add timeout?: number field to RequestParams

Test Results

Scenario Result
Without timeout: false + 6-min request ❌ Timeout at 5 minutes
With timeout: false + 6-min request ✅ Completed successfully after 6 minutes

Test Plan

  • [x] Verified 6-minute API request completes successfully with timeout: false
  • [ ] Test in Sim.AI UI with API block configured for 10-minute timeout
  • [ ] Verify Docker deployment works with long-running requests

Closes #2242

🤖 Generated with Claude Code

majiayu000 avatar Dec 29 '25 13:12 majiayu000

@majiayu000 is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot] avatar Dec 29 '25 13:12 vercel[bot]

Greptile Summary

Fixed the 5-minute timeout limit for API block requests by disabling Bun/Node.js default timeout and implementing user-configurable timeout control (2-10 minutes).

Key Changes:

  • Added parseTimeout() function to validate and clamp timeout values between 2-10 minutes
  • Applied timeout: false + AbortSignal.timeout() to handleInternalRequest and handleProxyRequest in tools/index.ts
  • Added maxDuration = 600 to /api/proxy and /api/workflows/[id]/execute routes for Next.js
  • Added timeout field to RequestParams type definition

Issues Found:

  • GET handler in /api/proxy/route.ts sets timeout: false but doesn't include AbortSignal.timeout(), creating inconsistent timeout behavior between GET and POST handlers

Confidence Score: 4/5

  • This PR is safe to merge with one logical fix needed for the GET handler
  • The core timeout fix is well-implemented with proper validation and documentation. However, the GET handler in apps/sim/app/api/proxy/route.ts is missing AbortSignal.timeout(), which creates inconsistent behavior - it has no timeout control except the route's maxDuration. This should be fixed to match the POST handlers' pattern of using both timeout: false and signal: AbortSignal.timeout().
  • apps/sim/app/api/proxy/route.ts GET handler needs AbortSignal.timeout() added for consistent timeout behavior

Important Files Changed

Filename Overview
apps/sim/tools/index.ts Added parseTimeout() function and timeout handling with timeout: false + AbortSignal.timeout() to both request handlers
apps/sim/app/api/proxy/route.ts Added maxDuration = 600 and timeout: false to GET handler, but missing AbortSignal for timeout control

Sequence Diagram

sequenceDiagram
    participant Client as API Block/Client
    participant ProxyRoute as /api/proxy
    participant ToolHandler as tools/index.ts
    participant ExternalAPI as External API
    
    Note over Client,ExternalAPI: User-configured timeout flow
    
    Client->>ProxyRoute: POST /api/proxy<br/>{toolId, params: {timeout: 600000}}
    Note over ProxyRoute: maxDuration = 600s<br/>(Next.js route config)
    
    ProxyRoute->>ToolHandler: executeTool(toolId, params)
    
    alt Internal Request Handler
        ToolHandler->>ToolHandler: parseTimeout(params.timeout)<br/>Returns: min(timeout, 600000ms)
        Note over ToolHandler: timeout: false<br/>signal: AbortSignal.timeout(timeoutMs)
        ToolHandler->>ExternalAPI: fetch(url, {timeout: false, signal})
        ExternalAPI-->>ToolHandler: Response (within timeout)
        ToolHandler-->>ProxyRoute: Success
    else Proxy Request Handler
        ToolHandler->>ToolHandler: parseTimeout(params.timeout)<br/>Returns: min(timeout, 600000ms)
        Note over ToolHandler: timeout: false<br/>signal: AbortSignal.timeout(timeoutMs)
        ToolHandler->>ProxyRoute: fetch(/api/proxy, {timeout: false, signal})
        ProxyRoute->>ExternalAPI: Forward request
        ExternalAPI-->>ProxyRoute: Response (within timeout)
        ProxyRoute-->>ToolHandler: Response
        ToolHandler-->>ProxyRoute: Success
    end
    
    ProxyRoute-->>Client: Response with result
    
    Note over Client,ExternalAPI: Timeout scenarios
    
    alt Timeout Exceeded
        ToolHandler->>ExternalAPI: fetch with AbortSignal
        Note over ExternalAPI: Request takes > timeoutMs
        ExternalAPI-xToolHandler: AbortError (signal timeout)
        ToolHandler-->>Client: Error: Request timed out
    end
    
    alt GET /api/proxy (Direct Proxy)
        Client->>ProxyRoute: GET /api/proxy?url=...
        Note over ProxyRoute: timeout: false<br/>NO AbortSignal!<br/>Relies only on maxDuration=600s
        ProxyRoute->>ExternalAPI: fetch(url, {timeout: false})
        ExternalAPI-->>ProxyRoute: Response
        ProxyRoute-->>Client: Response
    end

greptile-apps[bot] avatar Dec 29 '25 13:12 greptile-apps[bot]