mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-10 07:27:57 -05:00
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:
committed by
GitHub
parent
9f0584a818
commit
2f0509adaf
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user