Zie619/n8n-workflows · error · Error
Initial Vector 'data' is in an unsupported format.
Error message
Initial Vector 'data' is in an unsupported format.
What it means
Thrown by the 'Encrypt Return' Code node when the extracted IV data is neither a string, a Buffer, nor an Array. In n8n, data crossing node boundaries is JSON-serialized; a Buffer becomes `{type:'Buffer', data:[...]}`, so a plain object without a usable `.data`, a number, or `{type:'Buffer'}` with data in an unexpected shape lands in the else branch.
Source
Thrown at workflows/Code/0924_Code_Respondtowebhook_Process_Webhook.json:220
-1320,
860
],
"parameters": {
"jsCode": "const crypto = require(\"crypto\");\n\nconst privateKey = `-----BEGIN PRIVATE KEY-----\n[INSERT YOUR KEY HERE]\n-----END PRIVATE KEY-----`;\n\n// Convert input buffers\nconst encryptedAesKeyBuffer = Buffer.from($json.encryptedAesKey.data);\nconst initialVector = Buffer.from($json.initialVector.data);\nconst encryptedFlowData = Buffer.from($json.encryptedFlowData.data);\n\n// Check if encrypted AES key, IV, and encrypted flow data exist\nif (!encryptedAesKeyBuffer || !initialVector || !encryptedFlowData) {\n throw new Error(\"Missing required data (encrypted AES key, IV, or flow data)\");\n}\n\n// Decrypt AES key using RSA\nconst decryptedKey = crypto.privateDecrypt(\n {\n key: privateKey,\n padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,\n oaepHash: \"sha256\",\n },\n encryptedAesKeyBuffer\n);\n\n// Ensure AES key is exactly 16 bytes (AES-128 requires it)\nconst aesKey = decryptedKey.slice(0, 16);\nif (aesKey.length !== 16) {\n throw new Error(\"Invalid AES Key length\");\n}\n\n// Handle initialization vector (IV): If needed, flip the IV bits (standardize behavior)\nconst standardizedIv = Buffer.from(initialVector);\nif (standardizedIv.length !== 16) {\n throw new Error(\"Invalid IV length, must be 16 bytes\");\n}\n\n// Extract the last 16 bytes as the authentication tag (GCM uses 16-byte tags)\nconst authTag = encryptedFlowData.slice(-16);\nconst encryptedDataWithoutTag = encryptedFlowData.slice(0, -16);\n\n// AES Decryption\nconst decipher = crypto.createDecipheriv(\"aes-128-gcm\", aesKey, standardizedIv);\ndecipher.setAuthTag(authTag);\n\nlet decrypted;\ntry {\n decrypted = Buffer.concat([\n decipher.update(encryptedDataWithoutTag),\n decipher.final(),\n ]);\n} catch (error) {\n throw new Error(\"Decryption failed: \" + error.message);\n}\n\nreturn [{ \n decryptedPayload: decrypted.toString(\"utf-8\"),\n aesKey: aesKey.toString(\"base64\")\n}];\n"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},
{
"id": "17c055f3-c278-48c4-89d4-d305a35bc526",
"name": "Encrypt Return",
"type": "n8n-nodes-base.code",
"position": [
-200,
760
],
"parameters": {
"jsCode": "const crypto = require(\"crypto\");\n\n// Access initial_vector from the correct path\nconst initialVector = $('move to base64').first().json.initialVector;\n\nif (!initialVector) {\n throw new Error(\"Initial Vector is undefined or missing.\");\n}\n\n// Check if 'data' is a property of initialVector\nconst ivData = initialVector.data || initialVector; // Fallback to initialVector if no 'data' property\n\nif (!ivData) {\n throw new Error(\"Initial Vector 'data' is undefined or missing.\");\n}\n\n// Check for various formats of initialVector\nlet ivBuffer;\nif (typeof ivData === \"string\") {\n ivBuffer = Buffer.from(ivData, 'base64');\n} else if (Buffer.isBuffer(ivData)) {\n ivBuffer = ivData;\n} else if (Array.isArray(ivData)) {\n ivBuffer = Buffer.from(ivData);\n} else {\n throw new Error(\"Initial Vector 'data' is in an unsupported format.\");\n}\n\n// Invert Initialization Vector\nconst invertedIV = Buffer.from(ivBuffer.map((b) => ~b & 0xFF)); // Ensure the result stays a valid byte\n\n// Access AES Key from the correct path\nconst aesKeyBase64 = $('Decryption Code').first().json.aesKey || \"\";\nif (!aesKeyBase64) {\n throw new Error(\"AES Key is missing.\");\n}\n\nconst aesKey = Buffer.from(aesKeyBase64, \"base64\");\n\n// Extract data from the input with proper error handling\nlet date = \"2025-03-14\"; // Default fallback date\nlet startTimes = []; // Default empty array for start times\n\n// Check if $json exists and has the expected structure\nif ($json) {\n // Check if $json is an array\n if (Array.isArray($json) && $json.length > 0) {\n const appointmentData = $json[0];\n if (appointmentData && appointmentData.appointment_date) {\n date = appointmentData.appointment_date;\n }\n if (appointmentData && Array.isArray(appointmentData.start_times)) {\n startTimes = appointmentData.start_times;\n }\n } else if ($json.appointment_date) {\n // If $json is not an array but has appointment_date directly\n date = $json.appointment_date;\n if (Array.isArray($json.start_times)) {\n startTimes = $json.start_times;\n }\n }\n}\n\n// Log the structure of $json for debugging\nconsole.log(\"Input JSON structure:\", JSON.stringify($json, null, 2));\n\n// Ensure we have time slots (use defaults if none found)\nif (!startTimes.length) {\n console.log(\"No time slots found in input, using defaults\");\n startTimes = [\"12:00:00\", \"12:30:00\", \"13:30:00\", \"14:00:00\"];\n}\n\n// Map the time slots to the required format\nconst timeSlots = startTimes.map((timeString, index) => ({\n id: `time_${index + 1}`,\n title: timeString\n}));\n\n// Map the date slots for each time slot\nconst dateSlots = [{\n id: \"date_1\",\n title: date\n}];\n\n// Define the response data with the extracted time and date\nconst responseData = {\n status: \"active\",\n time: timeSlots,\n date: dateSlots\n};\n\n// Define the flow_token (accessed from the correct path)\nconst flowToken = $('Json Parser').first().json.flow_token || \"\"; // Fetch the flow_token dynamically from the path\n\nif (!flowToken) {\n throw new Error(\"Flow token is missing.\");\n}\n\n// Define the next screen (this should be based on your flow logic)\nconst nextScreen = \"APPOINTMENT\"; // You can set this dynamically depending on the flow\n\n// Define Response Message (updated to match the required response format)\nconst responseMessage = JSON.stringify({\n version: \"3.0\", // Fixed version as per your requirements\n action: \"data_exchange\", // Since we're responding to a data exchange request\n screen: nextScreen, // The next screen that the user will be redirected to\n data: responseData, // Data to send back (includes the time and date)\n flow_token: flowToken, // Flow token for session identification\n});\n\n// Encrypt Response using AES-GCM\nconst cipher = crypto.createCipheriv(\"aes-128-gcm\", aesKey, invertedIV);\nlet encryptedResponse = Buffer.concat([\n cipher.update(responseMessage, \"utf-8\"),\n cipher.final()\n]);\n\n// Get the authentication tag\nconst authTag = cipher.getAuthTag();\n\n// Append the authentication tag to the encrypted response\nconst result = Buffer.concat([encryptedResponse, authTag]);\n\n// Encode the entire response as Base64\nconst base64Response = result.toString(\"base64\");\n\n// Return the Base64-encoded response as the body\nreturn [{ body: base64Response }];\n"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},
{
"id": "412f55e3-5867-4e65-a494-3e3bf991d59c",
"name": "Encrypt Return1",
"type": "n8n-nodes-base.code",
"position": [
-200,
1000
],
"parameters": {
"jsCode": "const crypto = require(\"crypto\");\n\nconst jsonData = items;\n\n// Parse the decryptedPayload string into a JSON object\nconst decryptedPayload = JSON.parse(jsonData[0].json.originalPayload.decryptedPayload);\n\n// Extract the seats array\nconst seats = decryptedPayload.data.seats;\n\nif (!seats || !Array.isArray(seats) || seats.length === 0) {\n throw new Error(\"Seats data is missing or invalid.\");\n}\n\n// Access initial_vector from the correct path\nconst initialVector = $('move to base64').first().json.initialVector;\nif (!initialVector) {\n throw new Error(\"Initial Vector is undefined or missing.\");\n}\n\nconst ivData = initialVector.data || initialVector;\nif (!ivData) {\n throw new Error(\"Initial Vector 'data' is undefined or missing.\");\n}\n\nlet ivBuffer;\nif (typeof ivData === \"string\") {\n ivBuffer = Buffer.from(ivData, 'base64');\n} else if (Buffer.isBuffer(ivData)) {\n ivBuffer = ivData;\n} else if (Array.isArray(ivData)) {\n ivBuffer = Buffer.from(ivData);\n} else {\n throw new Error(\"Initial Vector 'data' is in an unsupported format.\");\n}\n\nconst invertedIV = Buffer.from(ivBuffer.map((b) => ~b & 0xFF));\n\n// Access AES Key from the correct path\nconst aesKeyBase64 = $('Decryption Code').first().json.aesKey || \"\";\nif (!aesKeyBase64) {\n throw new Error(\"AES Key is missing.\");\n}\nconst aesKey = Buffer.from(aesKeyBase64, \"base64\");\n\n// Define the response data with the extracted seats\nconst responseData = {\n status: \"active\",\n seats: seats.map((seat, index) => ({\n id: `seat_${index + 1}`,\n title: seat\n }))\n};\n\n// Define the flow_token\nconst flowToken = $('Json Parser').first().json.flow_token || \"\";\nif (!flowToken) {\n throw new Error(\"Flow token is missing.\");\n}\n\nconst nextScreen = \"SUMMARY\";\n\nconst responseMessage = JSON.stringify({\n version: \"3.0\",\n action: \"data_exchange\",\n screen: nextScreen,\n data: responseData,\n flow_token: flowToken,\n});\n\n// Encrypt Response using AES-GCM\nconst cipher = crypto.createCipheriv(\"aes-128-gcm\", aesKey, invertedIV);\nlet encryptedResponse = Buffer.concat([\n cipher.update(responseMessage, \"utf-8\"),\n cipher.final()\n]);\n\nconst authTag = cipher.getAuthTag();\nconst result = Buffer.concat([encryptedResponse, authTag]);\nconst base64Response = result.toString(\"base64\");\n\n// Return the encrypted response\nreturn [{ body: base64Response }];\n"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},View on GitHub (pinned to 94007c1445)
Solutions
- Log the type and value: console.log(typeof ivData, ivData?.type, Array.isArray(ivData?.data)) to identify the actual shape.
- Add a `{type:'Buffer'}` object branch: if (ivData.type === 'Buffer' && Array.isArray(ivData.data)) ivBuffer = Buffer.from(ivData.data);
- Pin the contract: make 'move to base64' return strings (initialVector.toString('base64')) so only the string branch is ever needed.
- Align both 'Encrypt Return' and 'Encrypt Return1' (and 'Decryption Code') on one canonical IV representation to avoid divergent format handling.
Example fix
// before
let ivBuffer;
if (typeof ivData === "string") { ivBuffer = Buffer.from(ivData, 'base64'); }
else if (Buffer.isBuffer(ivData)) { ivBuffer = ivData; }
else if (Array.isArray(ivData)) { ivBuffer = Buffer.from(ivData); }
else { throw new Error("Initial Vector 'data' is in an unsupported format."); }
// after
const toBuffer = (v) => {
if (typeof v === 'string') return Buffer.from(v, 'base64');
if (Buffer.isBuffer(v)) return v;
if (Array.isArray(v)) return Buffer.from(v);
if (v && v.type === 'Buffer' && Array.isArray(v.data)) return Buffer.from(v.data);
throw new Error(`Initial Vector in unsupported format: ${typeof v}`);
};
const ivBuffer = toBuffer(ivData); Defensive patterns
Strategy: type-guard
Validate before calling
const toBuffer = (v) => {
if (typeof v === 'string') return Buffer.from(v, 'base64');
if (Buffer.isBuffer(v)) return v;
if (Array.isArray(v)) return Buffer.from(v);
if (v && v.type === 'Buffer' && Array.isArray(v.data)) return Buffer.from(v.data);
return null;
}; Type guard
const isIVCompatible = (v) => typeof v === 'string' || Buffer.isBuffer(v) || Array.isArray(v) || (v && v.type === 'Buffer' && Array.isArray(v.data));
Try / catch
const ivBuffer = toBuffer(ivData);
if (!ivBuffer || ivBuffer.length !== 16) {
throw new Error(`IV unsupported format: ${Object.prototype.toString.call(ivData)}`);
} Prevention
- Pin one canonical IV representation (base64 string) across the whole workflow
- Include the received type in error messages for fast triage
- After any n8n upgrade, dry-run the flow and log JSON.stringify of cross-node data
- Unit-test the converter against string, array, and {type:'Buffer'} inputs
When it happens
Trigger: The upstream 'move to base64' node stored initialVector as a Buffer that n8n serialized to `{type:'Buffer',data:[...]}` (Array branch catches it) but a version/setting change serialized it as e.g. an object with base64 string under a different key, or initialVector ended up being a plain object like {} or a number after expression rewrites; also occurs when downstream code reads `.first().json` of a node whose output shape was edited.
Common situations: n8n version upgrades that change Buffer serialization in `$('node').first().json`; editing the upstream Code node to return a different structure without updating this node; copying this node into another workflow where the source node outputs base64 strings while this one expects Buffer-like data.
Related errors
- Initial Vector 'data' is undefined or missing.
- AES Key is missing.
- Flow token is missing.
- Seats data is missing or invalid.
- Insufficient stock for ${$('Retrieve Issue Request Details')
AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15).
Data as JSON: /api/errors/14476e95cc5520ac.
Report an issue: GitHub.