Skip to content

Commit 2f0509a

Browse files
icecrasher321Sg312
andauthored
fix(nextjs-size-limit): surface 413s accurately (#2265)
* fix(api-call-size-limit): cannot exceed nextjs size limits * fix * Convert to buffer --------- Co-authored-by: Siddharth Ganesan <[email protected]>
1 parent 9f0584a commit 2f0509a

File tree

1 file changed

+147
-2
lines changed

1 file changed

+147
-2
lines changed

apps/sim/tools/index.ts

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,84 @@ import {
1616

1717
const logger = createLogger('Tools')
1818

19+
/**
20+
* Maximum request body size in bytes before we warn/error about size limits.
21+
* Next.js 16 has a default middleware/proxy body limit of 10MB.
22+
*/
23+
const MAX_REQUEST_BODY_SIZE_BYTES = 10 * 1024 * 1024 // 10MB
24+
25+
/**
26+
* User-friendly error message for body size limit exceeded
27+
*/
28+
const BODY_SIZE_LIMIT_ERROR_MESSAGE =
29+
'Request body size limit exceeded (10MB). The workflow data is too large to process. Try reducing the size of variables, inputs, or data being passed between blocks.'
30+
31+
/**
32+
* Validates request body size and throws a user-friendly error if exceeded
33+
* @param body - The request body string to check
34+
* @param requestId - Request ID for logging
35+
* @param context - Context string for logging (e.g., toolId)
36+
* @throws Error if body size exceeds the limit
37+
*/
38+
function validateRequestBodySize(
39+
body: string | undefined,
40+
requestId: string,
41+
context: string
42+
): void {
43+
if (!body) return
44+
45+
const bodySize = Buffer.byteLength(body, 'utf8')
46+
if (bodySize > MAX_REQUEST_BODY_SIZE_BYTES) {
47+
const bodySizeMB = (bodySize / (1024 * 1024)).toFixed(2)
48+
const maxSizeMB = (MAX_REQUEST_BODY_SIZE_BYTES / (1024 * 1024)).toFixed(0)
49+
logger.error(`[${requestId}] Request body size exceeds limit for ${context}:`, {
50+
bodySize,
51+
bodySizeMB: `${bodySizeMB}MB`,
52+
maxSize: MAX_REQUEST_BODY_SIZE_BYTES,
53+
maxSizeMB: `${maxSizeMB}MB`,
54+
})
55+
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
56+
}
57+
}
58+
59+
/**
60+
* Checks if an error message indicates a body size limit issue
61+
* @param errorMessage - The error message to check
62+
* @returns true if the error is related to body size limits
63+
*/
64+
function isBodySizeLimitError(errorMessage: string): boolean {
65+
const lowerMessage = errorMessage.toLowerCase()
66+
return (
67+
lowerMessage.includes('body size') ||
68+
lowerMessage.includes('payload too large') ||
69+
lowerMessage.includes('entity too large') ||
70+
lowerMessage.includes('request entity too large') ||
71+
lowerMessage.includes('body_not_allowed') ||
72+
lowerMessage.includes('request body larger than')
73+
)
74+
}
75+
76+
/**
77+
* Handles body size limit errors by logging and throwing a user-friendly error
78+
* @param error - The original error
79+
* @param requestId - Request ID for logging
80+
* @param context - Context string for logging (e.g., toolId)
81+
* @throws Error with user-friendly message if it's a size limit error
82+
* @returns false if not a size limit error (caller should continue handling)
83+
*/
84+
function handleBodySizeLimitError(error: unknown, requestId: string, context: string): boolean {
85+
const errorMessage = error instanceof Error ? error.message : String(error)
86+
87+
if (isBodySizeLimitError(errorMessage)) {
88+
logger.error(`[${requestId}] Request body size limit exceeded for ${context}:`, {
89+
originalError: errorMessage,
90+
})
91+
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
92+
}
93+
94+
return false
95+
}
96+
1997
/**
2098
* System parameters that should be filtered out when extracting tool arguments
2199
* These are internal parameters used by the execution framework, not tool inputs
@@ -537,6 +615,9 @@ async function handleInternalRequest(
537615
const headers = new Headers(requestParams.headers)
538616
await addInternalAuthIfNeeded(headers, isInternalRoute, requestId, toolId)
539617

618+
// Check request body size before sending to detect potential size limit issues
619+
validateRequestBodySize(requestParams.body, requestId, toolId)
620+
540621
// Prepare request options
541622
const requestOptions = {
542623
method: requestParams.method,
@@ -548,6 +629,15 @@ async function handleInternalRequest(
548629

549630
// For non-OK responses, attempt JSON first; if parsing fails, fall back to text
550631
if (!response.ok) {
632+
// Check for 413 (Entity Too Large) - body size limit exceeded
633+
if (response.status === 413) {
634+
logger.error(`[${requestId}] Request body too large for ${toolId} (HTTP 413):`, {
635+
status: response.status,
636+
statusText: response.statusText,
637+
})
638+
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
639+
}
640+
551641
let errorData: any
552642
try {
553643
errorData = await response.json()
@@ -645,6 +735,9 @@ async function handleInternalRequest(
645735
error: undefined,
646736
}
647737
} catch (error: any) {
738+
// Check if this is a body size limit error and throw user-friendly message
739+
handleBodySizeLimitError(error, requestId, toolId)
740+
648741
logger.error(`[${requestId}] Internal request error for ${toolId}:`, {
649742
error: error instanceof Error ? error.message : String(error),
650743
})
@@ -737,13 +830,24 @@ async function handleProxyRequest(
737830
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
738831
await addInternalAuthIfNeeded(headers, true, requestId, `proxy:${toolId}`)
739832

833+
const body = JSON.stringify({ toolId, params, executionContext })
834+
835+
// Check request body size before sending
836+
validateRequestBodySize(body, requestId, `proxy:${toolId}`)
837+
740838
const response = await fetch(proxyUrl, {
741839
method: 'POST',
742840
headers,
743-
body: JSON.stringify({ toolId, params, executionContext }),
841+
body,
744842
})
745843

746844
if (!response.ok) {
845+
// Check for 413 (Entity Too Large) - body size limit exceeded
846+
if (response.status === 413) {
847+
logger.error(`[${requestId}] Request body too large for proxy:${toolId} (HTTP 413)`)
848+
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
849+
}
850+
747851
const errorText = await response.text()
748852
logger.error(`[${requestId}] Proxy request failed for ${toolId}:`, {
749853
status: response.status,
@@ -783,6 +887,9 @@ async function handleProxyRequest(
783887
const result = await response.json()
784888
return result
785889
} catch (error: any) {
890+
// Check if this is a body size limit error and throw user-friendly message
891+
handleBodySizeLimitError(error, requestId, `proxy:${toolId}`)
892+
786893
logger.error(`[${requestId}] Proxy request error for ${toolId}:`, {
787894
error: error instanceof Error ? error.message : String(error),
788895
})
@@ -880,6 +987,11 @@ async function executeMcpTool(
880987
workspaceId, // Pass workspace context for scoping
881988
}
882989

990+
const body = JSON.stringify(requestBody)
991+
992+
// Check request body size before sending
993+
validateRequestBodySize(body, actualRequestId, `mcp:${toolId}`)
994+
883995
logger.info(`[${actualRequestId}] Making MCP tool request to ${toolName} on ${serverId}`, {
884996
hasWorkspaceId: !!workspaceId,
885997
hasWorkflowId: !!workflowId,
@@ -888,14 +1000,29 @@ async function executeMcpTool(
8881000
const response = await fetch(`${baseUrl}/api/mcp/tools/execute`, {
8891001
method: 'POST',
8901002
headers,
891-
body: JSON.stringify(requestBody),
1003+
body,
8921004
})
8931005

8941006
const endTime = new Date()
8951007
const endTimeISO = endTime.toISOString()
8961008
const duration = endTime.getTime() - new Date(actualStartTime).getTime()
8971009

8981010
if (!response.ok) {
1011+
// Check for 413 (Entity Too Large) - body size limit exceeded
1012+
if (response.status === 413) {
1013+
logger.error(`[${actualRequestId}] Request body too large for mcp:${toolId} (HTTP 413)`)
1014+
return {
1015+
success: false,
1016+
output: {},
1017+
error: BODY_SIZE_LIMIT_ERROR_MESSAGE,
1018+
timing: {
1019+
startTime: actualStartTime,
1020+
endTime: endTimeISO,
1021+
duration,
1022+
},
1023+
}
1024+
}
1025+
8991026
let errorMessage = `MCP tool execution failed: ${response.status} ${response.statusText}`
9001027

9011028
try {
@@ -950,6 +1077,24 @@ async function executeMcpTool(
9501077
const endTimeISO = endTime.toISOString()
9511078
const duration = endTime.getTime() - new Date(actualStartTime).getTime()
9521079

1080+
// Check if this is a body size limit error
1081+
const errorMsg = error instanceof Error ? error.message : String(error)
1082+
if (isBodySizeLimitError(errorMsg)) {
1083+
logger.error(`[${actualRequestId}] Request body size limit exceeded for mcp:${toolId}:`, {
1084+
originalError: errorMsg,
1085+
})
1086+
return {
1087+
success: false,
1088+
output: {},
1089+
error: BODY_SIZE_LIMIT_ERROR_MESSAGE,
1090+
timing: {
1091+
startTime: actualStartTime,
1092+
endTime: endTimeISO,
1093+
duration,
1094+
},
1095+
}
1096+
}
1097+
9531098
logger.error(`[${actualRequestId}] Error executing MCP tool ${toolId}:`, error)
9541099

9551100
const errorMessage =

0 commit comments

Comments
 (0)