/n8n-tutorials

How to fix workflow crashes on large responses from Gemini in n8n?

Learn how to fix workflow crashes in n8n caused by large Gemini AI responses with error handling, timeout settings, pagination, streaming, and optimized prompts for stable processing.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free consultation

How to fix workflow crashes on large responses from Gemini in n8n?

To fix workflow crashes on large responses from Gemini in n8n, you need to implement proper error handling, increase timeout settings, and use pagination or streaming techniques. This prevents workflow failures when Gemini returns extensive data that exceeds n8n's default processing limits.

 

Comprehensive Guide to Fix Workflow Crashes on Large Responses from Gemini in n8n

 

Step 1: Understanding the Problem

 

When working with Gemini AI in n8n workflows, large responses can cause crashes due to several reasons:

  • Memory limitations in n8n
  • Default timeout constraints
  • Lack of proper error handling
  • Binary data management issues

These issues typically manifest as workflow execution failures, timeouts, or "out of memory" errors.

 

Step 2: Update n8n to the Latest Version

 

Ensure you're running the latest version of n8n as newer versions often include performance improvements and bug fixes.

  • For self-hosted installations:

# Using npm
npm update n8n -g

# Using Docker
docker pull n8nio/n8n:latest
docker-compose pull # If using docker-compose

 

Step 3: Configure Environment Variables for Better Performance

 

Modify n8n's environment variables to handle larger payloads and extend timeouts:


# For standalone installations, add these to your .env file:
EXECUTIONS\_PROCESS=main
N8N\_METRICS=true
NODE\_OPTIONS="--max-old-space-size=4096"
WEBHOOK\_TIMEOUT=60000

For Docker installations, add these environment variables to your docker-compose.yml:


services:
  n8n:
    image: n8nio/n8n
    environment:
    - EXECUTIONS\_PROCESS=main
    - N8N\_METRICS=true
    - NODE\_OPTIONS=--max-old-space-size=4096
    - WEBHOOK\_TIMEOUT=60000

 

Step 4: Implement a Function Node for Chunked Processing

 

Add a Function node after your Gemini AI node to break down large responses:


// Function to split large responses into manageable chunks
const maxChunkSize = 1000000; // 1MB chunks, adjust as needed
const response = items[0].json.response || items[0].json;

// Check if response needs chunking
if (JSON.stringify(response).length > maxChunkSize) {
  // For text responses
  if (typeof response === 'string') {
    const chunks = [];
    for (let i = 0; i < response.length; i += maxChunkSize) {
      chunks.push(response.substring(i, i + maxChunkSize));
    }
    
    return chunks.map(chunk => ({
      json: {
        chunk,
        isChunked: true,
        totalChunks: chunks.length
      }
    }));
  } 
  // For object responses
  else {
    const responseStr = JSON.stringify(response);
    const chunks = [];
    
    for (let i = 0; i < responseStr.length; i += maxChunkSize) {
      chunks.push(responseStr.substring(i, i + maxChunkSize));
    }
    
    return chunks.map((chunk, index) => ({
      json: {
        chunk: index === 0 ? JSON.parse(chunk + '}') : JSON.parse('{' + chunk),
        isChunked: true,
        chunkIndex: index,
        totalChunks: chunks.length
      }
    }));
  }
}

// Return original response if no chunking needed
return [{json: response}];

 

Step 5: Add Error Handling with Error Trigger Nodes

 

Create a robust error handling system:

  1. In your workflow, add an "Error Trigger" node
  2. Connect it to a "Function" node with this code:

// Log the error details
console.log('Error occurred:', $input.all()[0].json.error);

// Extract workflow details
const workflowId = $input.all()[0].json.workflow.id;
const executionId = $input.all()[0].json.execution.id;
const errorMessage = $input.all()[0].json.error.message;
const errorStack = $input.all()[0].json.error.stack;

// Create a structured error response
return [{
  json: {
    workflowId,
    executionId,
    errorMessage,
    errorStack,
    timestamp: new Date().toISOString(),
    recommendation: "The Gemini AI response was likely too large. Consider adding pagination or reducing the complexity of your prompt."
  }
}];
  1. Add a "Send Email" or "Send Slack Message" node to notify yourself of errors

 

Step 6: Implement Pagination for Gemini Requests

 

For very large expected responses, implement pagination:


// In a Function node before your Gemini AI node
const fullPrompt = items[0].json.prompt;
const maxPromptLength = 5000; // Adjust based on your use case
const chunks = [];

// Split the prompt into chunks if needed
if (fullPrompt.length > maxPromptLength) {
  for (let i = 0; i < fullPrompt.length; i += maxPromptLength) {
    chunks.push(fullPrompt.substring(i, i + maxPromptLength));
  }
  
  // Only send the first chunk initially
  return [{
    json: {
      prompt: chunks[0],
      remainingChunks: chunks.slice(1),
      chunkIndex: 0,
      totalChunks: chunks.length
    }
  }];
} else {
  // No chunking needed
  return [{
    json: {
      prompt: fullPrompt,
      remainingChunks: [],
      chunkIndex: 0,
      totalChunks: 1
    }
  }];
}

Then, handle subsequent chunks in a separate workflow or loop:


// In a Function node after your Gemini AI node
const currentResult = items[0].json;
const remainingChunks = items[0].json.remainingChunks || [];
const chunkIndex = items[0].json.chunkIndex;
const totalChunks = items[0].json.totalChunks;

// Store the current response
const responseKey = `geminiResponse_part${chunkIndex}`;
$node.context[responseKey] = currentResult.response || currentResult.text || currentResult.content;

// Check if we have more chunks to process
if (remainingChunks.length > 0) {
  // Process the next chunk
  return [{
    json: {
      prompt: remainingChunks[0],
      remainingChunks: remainingChunks.slice(1),
      chunkIndex: chunkIndex + 1,
      totalChunks: totalChunks
    }
  }];
} else {
  // All chunks processed, combine results
  let finalResponse = '';
  for (let i = 0; i < totalChunks; i++) {
    finalResponse += $node.context[`geminiResponse_part${i}`] || '';
  }
  
  return [{
    json: {
      response: finalResponse,
      isComplete: true
    }
  }];
}

 

Step 7: Use the Split In Batches Node for Large Data Processing

 

If you're processing multiple items with Gemini AI:

  1. Add a "Split In Batches" node before your Gemini AI node
  2. Configure it with these settings:
  • Batch Size: 1 (for sequential processing)
  • Options: Check "Return All Items"

This processes one request at a time, reducing memory usage.

 

Step 8: Implement Custom Timeout and Retry Logic

 

Add retry capabilities for Gemini API timeouts:


// In a Function node before Gemini AI node
const maxRetries = 3;
const retryCount = $node.context.retryCount || 0;

if (retryCount > 0) {
  console.log(`Retry attempt ${retryCount} of ${maxRetries} for Gemini request`);
}

// Pass the retry count along with the data
return [{
  json: {
    ...items[0].json,
    retryCount: retryCount
  }
}];

Then, after the Gemini AI node, add another Function node:


// Check if the request succeeded
if (items[0].json.error) {
  const retryCount = items[0].json.retryCount || 0;
  const maxRetries = 3;
  
  if (retryCount < maxRetries) {
    // Increment retry count and try again
    $node.context.retryCount = retryCount + 1;
    
    // Add exponential backoff (in milliseconds)
    const backoff = Math.pow(2, retryCount) \* 1000;
    console.log(`Request failed. Retrying in ${backoff}ms...`);
    
    // Wait before retrying
    return new Promise(resolve => {
      setTimeout(() => {
        resolve([{
          json: {
            ...items[0].json,
            retryCount: retryCount + 1
          }
        }]);
      }, backoff);
    });
  } else {
    // Max retries reached, forward the error
    console.log('Max retries reached. Giving up.');
    return [{
      json: {
        error: items[0].json.error,
        maxRetriesReached: true
      }
    }];
  }
}

// Reset retry count on success
$node.context.retryCount = 0;

// Return the successful response
return [items[0]];

 

Step 9: Optimize Gemini Prompts to Reduce Response Size

 

Modify your prompts to Gemini to limit response size:


// Example of a Function node to optimize prompts
const originalPrompt = items[0].json.prompt;

// Add size constraints to your prompt
const optimizedPrompt = \`${originalPrompt}

Please follow these guidelines in your response:
1. Keep your response under 1000 words
2. Avoid long lists or tables
3. Focus on the most important information
4. Use concise language
5. If the answer would be very long, provide a summary instead and indicate where full details were omitted\`;

return [{
  json: {
    prompt: optimizedPrompt,
    originalPrompt: originalPrompt
  }
}];

 

Step 10: Handle Binary Data Properly

 

If Gemini is generating binary data (like images), use proper handling:


// In a Function node after Gemini AI
if (items[0].json.binaryData || items[0].json.base64Data) {
  // Convert base64 to binary if needed
  const base64Data = items[0].json.base64Data || items[0].json.binaryData;
  const binaryBuffer = Buffer.from(base64Data, 'base64');
  
  // Create binary item properly
  return [{
    json: {
      success: true,
      size: binaryBuffer.length
    },
    binary: {
      data: {
        data: binaryBuffer,
        mimeType: 'application/octet-stream', // Change as needed
        fileName: 'gemini\_response.bin' // Change as needed
      }
    }
  }];
}

// Regular response handling
return [items[0]];

 

Step 11: Implement a Streaming Approach for Very Large Responses

 

For extremely large responses, implement a streaming approach:

  1. Split your Gemini processing into multiple sequential API calls
  2. Add a "Function" node that builds on each response:

// Initialize the storage context if needed
if (!$node.context.streamedResponse) {
  $node.context.streamedResponse = '';
}

// Append the current response
const currentResponse = items[0].json.response || items[0].json.text || '';
$node.context.streamedResponse += currentResponse;

// Check if we should continue streaming or complete
const shouldContinue = items[0].json.continue !== false;

if (shouldContinue) {
  // Prepare the next prompt part (e.g., "continue from where you left off")
  return [{
    json: {
      prompt: "Continue from where you left off.",
      continue: true,
      accumulated: $node.context.streamedResponse.length
    }
  }];
} else {
  // Stream complete - return full response
  const fullResponse = $node.context.streamedResponse;
  
  // Reset for next workflow run
  $node.context.streamedResponse = '';
  
  return [{
    json: {
      response: fullResponse,
      status: "complete",
      size: fullResponse.length
    }
  }];
}

 

Step 12: Add Monitoring and Logging

 

Implement detailed logging for troubleshooting:


// Function node for logging Gemini requests
const payload = {
  prompt: items[0].json.prompt,
  timestamp: new Date().toISOString(),
  workflowId: $workflow.id,
  executionId: $execution.id,
  promptSize: items[0].json.prompt.length
};

console.log('Gemini Request:', JSON.stringify(payload));

// Store in context for comparison with response
$node.context.requestDetails = payload;

return [items[0]];

Add another Function node after the Gemini AI node:


// Function node for logging Gemini responses
const requestDetails = $node.context.requestDetails || {};
const responseSize = JSON.stringify(items[0].json).length;

const logEntry = {
  ...requestDetails,
  responseTimestamp: new Date().toISOString(),
  responseSize: responseSize,
  responseStatus: items[0].json.error ? 'error' : 'success',
  executionTimeMs: new Date() - new Date(requestDetails.timestamp)
};

console.log('Gemini Response Stats:', JSON.stringify(logEntry));

// Alert on large responses
if (responseSize > 5000000) { // 5MB
  console.warn('Very large response detected:', responseSize, 'bytes');
}

return [items[0]];

 

Step 13: Use the HTTP Request Node Instead of Gemini Node for More Control

 

Sometimes using the HTTP Request node directly provides better control:

  1. Add an HTTP Request node with these settings:

{
  "contents": [
    {
      "parts": [
        {
          "text": "{{$json.prompt}}"
        }
      ]
    }
  ],
  "generationConfig": {
    "temperature": 0.7,
    "maxOutputTokens": 2048,
    "topP": 0.95,
    "topK": 40
  },
  "safetySettings": [
    {
      "category": "HARM_CATEGORY_HARASSMENT",
      "threshold": "BLOCK_MEDIUM_AND\_ABOVE"
    }
  ]
}
  1. Set Request Timeout to a higher value (e.g., 120000 for 2 minutes)

 

Step 14: Add a Timeout Safety Node

 

Create a timeout safety mechanism:


// In a Function node before Gemini AI
// Set maximum execution time (in milliseconds)
const maxExecutionTime = 60000; // 60 seconds
const startTime = new Date().getTime();

// Store the start time in node context
$node.context.geminiStartTime = startTime;

return [items[0]];

Then, after Gemini AI node, add:


// Check execution time
const startTime = $node.context.geminiStartTime || 0;
const endTime = new Date().getTime();
const executionTime = endTime - startTime;

console.log(`Gemini execution time: ${executionTime}ms`);

// Check if we're approaching timeout
if (executionTime > 50000) { // 50 seconds (warning threshold)
  console.warn('Warning: Gemini execution time approaching timeout limit');
}

return [items[0]];

 

Step 15: Set Up a Fallback System

 

Create a fallback system for when Gemini responses are too large:

  1. Add an "IF" node after your Gemini AI node with this condition:
    ```
    {{JSON.stringify($json).length > 5000000}}
    ```

  2. In the "true" branch, add a Function node:


// For oversized responses, create a simplified version
const originalResponse = items[0].json;
const responseSize = JSON.stringify(originalResponse).length;

// Create a simplified response
const simplifiedResponse = {
  truncated: true,
  originalSize: responseSize,
  summary: "The response was too large to process completely.",
  firstPortion: JSON.stringify(originalResponse).substring(0, 10000) + "..."
};

return [{
  json: simplifiedResponse
}];
  1. Connect both branches to continue your workflow

 

Step 16: Implement a Response Validator

 

Add validation to ensure responses are properly formatted:


// Function to validate Gemini responses
function validateResponse(response) {
  // Check if response exists
  if (!response) {
    return { valid: false, error: "Empty response" };
  }
  
  // Check for response structure
  if (response.error) {
    return { valid: false, error: response.error };
  }
  
  // Try to extract the actual content based on Gemini's response format
  const content = response.candidates?.[0]?.content?.parts?.[0]?.text || 
                  response.response || 
                  response.text ||
                  response.content ||
                  response;
                  
  // Check content size
  const contentSize = typeof content === 'string' ? 
    content.length : 
    JSON.stringify(content).length;
    
  if (contentSize > 10000000) { // 10MB
    return { 
      valid: false, 
      error: "Response too large", 
      size: contentSize 
    };
  }
  
  return { valid: true, content, size: contentSize };
}

// Validate the response
const validationResult = validateResponse(items[0].json);

if (!validationResult.valid) {
  console.error("Invalid Gemini response:", validationResult.error);
  return [{
    json: {
      error: validationResult.error,
      validationFailed: true,
      originalResponse: items[0].json
    }
  }];
}

// Return validated content
return [{
  json: {
    content: validationResult.content,
    size: validationResult.size,
    validated: true
  }
}];

 

Step 17: Implement a Database Storage Solution for Large Responses

 

For extremely large responses, store them in a database:

  1. Add a Function node to prepare the database entry:

// Prepare large response for database storage
const response = items[0].json;
const responseSize = JSON.stringify(response).length;

// If response is very large, prepare for database storage
if (responseSize > 1000000) { // 1MB threshold
  return [{
    json: {
      data_for_storage: {
        gemini\_response: response,
        timestamp: new Date().toISOString(),
        size\_bytes: responseSize,
        workflow\_id: $workflow.id,
        execution\_id: $execution.id
      },
      original\_request: $node.context.requestDetails || {},
      storeInDb: true
    }
  }];
} else {
  // Response is manageable, proceed normally
  return [items[0]];
}
  1. Add a Database node (MySQL, PostgreSQL, etc.) connected to the "true" branch of an IF node that checks for {{$json.storeInDb}} condition

  2. Add a Function node after database storage:


// Return a reference to the stored data
const dbOperationResult = items[0].json;
const insertId = dbOperationResult.insertId || dbOperationResult.id;

return [{
  json: {
    response\_reference: {
      id: insertId,
      stored\_at: new Date().toISOString(),
      original\_size: $node.context.responseSize,
      status: "stored_in_database"
    },
    retrieve\_instructions: "Use this ID to fetch the complete response from the database"
  }
}];

 

Step 18: Implement Progressive Loading for UI Integrations

 

If using n8n with a UI application, implement progressive loading:


// Function to prepare progressive loading responses
const totalResponseSize = JSON.stringify(items[0].json).length;
const maxChunkSize = 100000; // 100KB chunks

// If response is small enough, return directly
if (totalResponseSize <= maxChunkSize) {
  return [{
    json: {
      ...items[0].json,
      loadingComplete: true,
      progressiveLoading: false
    }
  }];
}

// For large responses, prepare progressive loading
const responseStr = JSON.stringify(items[0].json);
const totalChunks = Math.ceil(totalResponseSize / maxChunkSize);

// Store in context for subsequent requests
$node.context.fullResponse = responseStr;
$node.context.totalChunks = totalChunks;

// Return the first chunk with loading information
return [{
  json: {
    responseChunk: JSON.parse(responseStr.substring(0, maxChunkSize) + '}'),
    loadingStatus: {
      progressiveLoading: true,
      chunk: 1,
      totalChunks: totalChunks,
      percentComplete: Math.round((1 / totalChunks) \* 100),
      nextChunkUrl: `${$env.N8N_HOST}/webhook/progressive-loading?workflowId=${$workflow.id}&chunk=2`
    }
  }
}];

Then create a separate webhook workflow for fetching subsequent chunks:


// In a Function node in your webhook workflow
const requestedChunk = $input.item.params.chunk || 1;
const workflowId = $input.item.params.workflowId;

// Retrieve stored response from the original workflow
// This requires workflow data sharing or database retrieval

// Mock example (in real usage, fetch from database):
const fullResponse = $node.context.fullResponse;
const totalChunks = $node.context.totalChunks;
const maxChunkSize = 100000;

if (!fullResponse) {
  return [{
    json: {
      error: "Response data not found or expired",
      workflowId,
      requestedChunk
    }
  }];
}

// Calculate the start and end positions for this chunk
const startPos = (requestedChunk - 1) \* maxChunkSize;
const endPos = Math.min(startPos + maxChunkSize, fullResponse.length);
const isLastChunk = requestedChunk >= totalChunks;

// Extract the chunk
let chunkData;
try {
  if (startPos === 0) {
    chunkData = JSON.parse(fullResponse.substring(startPos, endPos) + '}');
  } else if (isLastChunk) {
    chunkData = JSON.parse('{' + fullResponse.substring(startPos));
  } else {
    chunkData = JSON.parse('{' + fullResponse.substring(startPos, endPos) + '}');
  }
} catch (error) {
  chunkData = { error: "Error parsing chunk data", details: error.message };
}

return [{
  json: {
    responseChunk: chunkData,
    loadingStatus: {
      progressiveLoading: true,
      chunk: parseInt(requestedChunk),
      totalChunks: totalChunks,
      percentComplete: Math.round((parseInt(requestedChunk) / totalChunks) \* 100),
      isComplete: isLastChunk,
      nextChunkUrl: isLastChunk ? null : 
        `${$env.N8N_HOST}/webhook/progressive-loading?workflowId=${workflowId}&chunk=${parseInt(requestedChunk) + 1}`
    }
  }
}];

 

Step 19: Schedule Regular Cleanup Tasks

 

Set up a scheduled workflow to clean up stored large responses:


// In a Function node in a scheduled cleanup workflow
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 7); // 7 days retention

const cutoffTimestamp = cutoffDate.toISOString();

// Return query parameters for database deletion
return [{
  json: {
    query\_params: {
      table: "gemini\_responses",
      delete\_condition: `created_at < '${cutoffTimestamp}'`
    }
  }
}];

Connect this to your database node to execute the cleanup.

 

Step 20: Regular Maintenance and Monitoring

 

Implement ongoing maintenance:

  1. Set up monitoring of your n8n system resources:
  • CPU usage
  • Memory consumption
  • Disk space
  • Network traffic
  1. Create a health check workflow that runs periodically:

// Health check function
const healthMetrics = {
  timestamp: new Date().toISOString(),
  systemInfo: {
    platform: process.platform,
    architecture: process.arch,
    nodeVersion: process.version,
    cpuUsage: process.cpuUsage(),
    memoryUsage: process.memoryUsage(),
    uptime: process.uptime()
  },
  n8nVersion: process.env.N8N\_VERSION || 'unknown'
};

// Check for warning signs
const memoryWarning = (healthMetrics.systemInfo.memoryUsage.heapUsed / 
                        healthMetrics.systemInfo.memoryUsage.heapTotal) > 0.85;

return [{
  json: {
    ...healthMetrics,
    status: memoryWarning ? 'warning' : 'healthy',
    warnings: memoryWarning ? ['High memory usage detected'] : []
  }
}];
  1. Connect the health check to notification nodes to alert you of issues

 

Conclusion

 

By implementing these steps, you'll create a robust n8n workflow system capable of handling large responses from Gemini AI. The key strategies are:

  • Chunking large responses into manageable pieces
  • Implementing proper error handling and retry logic
  • Using streaming and pagination techniques for large data
  • Optimizing Gemini prompts to control response size
  • Setting up appropriate environment variables for n8n
  • Creating fallback mechanisms for when responses are too large
  • Implementing monitoring and regular maintenance

By combining these techniques, your n8n workflows will be more resilient and capable of handling even the largest Gemini AI responses without crashing.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022