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 <siddharthganesan@gmail.com>
This commit is contained in:
Vikhyath Mondreti
2025-12-08 20:54:25 -08:00
committed by GitHub
parent 9f0584a818
commit 2f0509adaf

View File

@@ -16,6 +16,84 @@ import {
const logger = createLogger('Tools')
/**
* Maximum request body size in bytes before we warn/error about size limits.
* Next.js 16 has a default middleware/proxy body limit of 10MB.
*/
const MAX_REQUEST_BODY_SIZE_BYTES = 10 * 1024 * 1024 // 10MB
/**
* User-friendly error message for body size limit exceeded
*/
const BODY_SIZE_LIMIT_ERROR_MESSAGE =
'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.'
/**
* Validates request body size and throws a user-friendly error if exceeded
* @param body - The request body string to check
* @param requestId - Request ID for logging
* @param context - Context string for logging (e.g., toolId)
* @throws Error if body size exceeds the limit
*/
function validateRequestBodySize(
body: string | undefined,
requestId: string,
context: string
): void {
if (!body) return
const bodySize = Buffer.byteLength(body, 'utf8')
if (bodySize > MAX_REQUEST_BODY_SIZE_BYTES) {
const bodySizeMB = (bodySize / (1024 * 1024)).toFixed(2)
const maxSizeMB = (MAX_REQUEST_BODY_SIZE_BYTES / (1024 * 1024)).toFixed(0)
logger.error(`[${requestId}] Request body size exceeds limit for ${context}:`, {
bodySize,
bodySizeMB: `${bodySizeMB}MB`,
maxSize: MAX_REQUEST_BODY_SIZE_BYTES,
maxSizeMB: `${maxSizeMB}MB`,
})
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
}
}
/**
* Checks if an error message indicates a body size limit issue
* @param errorMessage - The error message to check
* @returns true if the error is related to body size limits
*/
function isBodySizeLimitError(errorMessage: string): boolean {
const lowerMessage = errorMessage.toLowerCase()
return (
lowerMessage.includes('body size') ||
lowerMessage.includes('payload too large') ||
lowerMessage.includes('entity too large') ||
lowerMessage.includes('request entity too large') ||
lowerMessage.includes('body_not_allowed') ||
lowerMessage.includes('request body larger than')
)
}
/**
* Handles body size limit errors by logging and throwing a user-friendly error
* @param error - The original error
* @param requestId - Request ID for logging
* @param context - Context string for logging (e.g., toolId)
* @throws Error with user-friendly message if it's a size limit error
* @returns false if not a size limit error (caller should continue handling)
*/
function handleBodySizeLimitError(error: unknown, requestId: string, context: string): boolean {
const errorMessage = error instanceof Error ? error.message : String(error)
if (isBodySizeLimitError(errorMessage)) {
logger.error(`[${requestId}] Request body size limit exceeded for ${context}:`, {
originalError: errorMessage,
})
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
}
return false
}
/**
* System parameters that should be filtered out when extracting tool arguments
* These are internal parameters used by the execution framework, not tool inputs
@@ -537,6 +615,9 @@ async function handleInternalRequest(
const headers = new Headers(requestParams.headers)
await addInternalAuthIfNeeded(headers, isInternalRoute, requestId, toolId)
// Check request body size before sending to detect potential size limit issues
validateRequestBodySize(requestParams.body, requestId, toolId)
// Prepare request options
const requestOptions = {
method: requestParams.method,
@@ -548,6 +629,15 @@ async function handleInternalRequest(
// For non-OK responses, attempt JSON first; if parsing fails, fall back to text
if (!response.ok) {
// Check for 413 (Entity Too Large) - body size limit exceeded
if (response.status === 413) {
logger.error(`[${requestId}] Request body too large for ${toolId} (HTTP 413):`, {
status: response.status,
statusText: response.statusText,
})
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
}
let errorData: any
try {
errorData = await response.json()
@@ -645,6 +735,9 @@ async function handleInternalRequest(
error: undefined,
}
} catch (error: any) {
// Check if this is a body size limit error and throw user-friendly message
handleBodySizeLimitError(error, requestId, toolId)
logger.error(`[${requestId}] Internal request error for ${toolId}:`, {
error: error instanceof Error ? error.message : String(error),
})
@@ -737,13 +830,24 @@ async function handleProxyRequest(
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
await addInternalAuthIfNeeded(headers, true, requestId, `proxy:${toolId}`)
const body = JSON.stringify({ toolId, params, executionContext })
// Check request body size before sending
validateRequestBodySize(body, requestId, `proxy:${toolId}`)
const response = await fetch(proxyUrl, {
method: 'POST',
headers,
body: JSON.stringify({ toolId, params, executionContext }),
body,
})
if (!response.ok) {
// Check for 413 (Entity Too Large) - body size limit exceeded
if (response.status === 413) {
logger.error(`[${requestId}] Request body too large for proxy:${toolId} (HTTP 413)`)
throw new Error(BODY_SIZE_LIMIT_ERROR_MESSAGE)
}
const errorText = await response.text()
logger.error(`[${requestId}] Proxy request failed for ${toolId}:`, {
status: response.status,
@@ -783,6 +887,9 @@ async function handleProxyRequest(
const result = await response.json()
return result
} catch (error: any) {
// Check if this is a body size limit error and throw user-friendly message
handleBodySizeLimitError(error, requestId, `proxy:${toolId}`)
logger.error(`[${requestId}] Proxy request error for ${toolId}:`, {
error: error instanceof Error ? error.message : String(error),
})
@@ -880,6 +987,11 @@ async function executeMcpTool(
workspaceId, // Pass workspace context for scoping
}
const body = JSON.stringify(requestBody)
// Check request body size before sending
validateRequestBodySize(body, actualRequestId, `mcp:${toolId}`)
logger.info(`[${actualRequestId}] Making MCP tool request to ${toolName} on ${serverId}`, {
hasWorkspaceId: !!workspaceId,
hasWorkflowId: !!workflowId,
@@ -888,7 +1000,7 @@ async function executeMcpTool(
const response = await fetch(`${baseUrl}/api/mcp/tools/execute`, {
method: 'POST',
headers,
body: JSON.stringify(requestBody),
body,
})
const endTime = new Date()
@@ -896,6 +1008,21 @@ async function executeMcpTool(
const duration = endTime.getTime() - new Date(actualStartTime).getTime()
if (!response.ok) {
// Check for 413 (Entity Too Large) - body size limit exceeded
if (response.status === 413) {
logger.error(`[${actualRequestId}] Request body too large for mcp:${toolId} (HTTP 413)`)
return {
success: false,
output: {},
error: BODY_SIZE_LIMIT_ERROR_MESSAGE,
timing: {
startTime: actualStartTime,
endTime: endTimeISO,
duration,
},
}
}
let errorMessage = `MCP tool execution failed: ${response.status} ${response.statusText}`
try {
@@ -950,6 +1077,24 @@ async function executeMcpTool(
const endTimeISO = endTime.toISOString()
const duration = endTime.getTime() - new Date(actualStartTime).getTime()
// Check if this is a body size limit error
const errorMsg = error instanceof Error ? error.message : String(error)
if (isBodySizeLimitError(errorMsg)) {
logger.error(`[${actualRequestId}] Request body size limit exceeded for mcp:${toolId}:`, {
originalError: errorMsg,
})
return {
success: false,
output: {},
error: BODY_SIZE_LIMIT_ERROR_MESSAGE,
timing: {
startTime: actualStartTime,
endTime: endTimeISO,
duration,
},
}
}
logger.error(`[${actualRequestId}] Error executing MCP tool ${toolId}:`, error)
const errorMessage =