{"record":{"id":"036dfcd2ff4d7596","repo":"Zie619/n8n-workflows","slug":"author-not-found","errorCode":null,"errorMessage":"Author not found","messagePattern":"Author not found","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workflows/Code/1864_Code_Executecommand_Create_Webhook.json","lineNumber":434,"sourceCode":"        \"options\": {\n          \"append\": false\n        },\n        \"fileName\": \"music1.mp3\",\n        \"operation\": \"write\"\n      },\n      \"typeVersion\": 1,\n      \"notes\": \"This readWriteFile node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"9fdca64e-79d7-4a58-91dc-4aa9f9b3c4cc\",\n      \"name\": \"Prepare Overlay Text (Quote & Author)\",\n      \"type\": \"n8n-nodes-base.code\",\n      \"position\": [\n        1620,\n        20\n      ],\n      \"parameters\": {\n        \"jsCode\": \"// Define separate configuration for the quote and the author\\nconst quoteFont = \\\"Kanit-Italic.ttf\\\";      // Font for the quote\\nconst quoteFontSize = 70;\\nconst authorFont = \\\"Kanit-Italic.ttf\\\";         // Font for the author (ensure this supports Thai)\\nconst authorFontSize = 50;\\nconst fontColor = \\\"white\\\";\\nconst lineHeightMultiplier = 1.1;\\nconst videoWidth = 1080;\\nconst margin = 40;  // Gap from left and right edges\\n\\n// Effective width for the quote text (accounting for left/right margins)\\nconst effectiveVideoWidth = videoWidth - 2 * margin;\\n\\n// Estimate average character width based on quoteFontSize (this is a rough estimate)\\nconst avgCharWidth = quoteFontSize * 0.6;\\nconst maxCharsPerLine = Math.floor(effectiveVideoWidth / avgCharWidth);\\n\\n// Retrieve the quote transcript and author from the \\\"Merge\\\" node\\nconst transcript = $node[\\\"Merge File Selection Data\\\"].json[\\\"Qoute\\\"];\\nif (!transcript) {\\n  throw new Error(\\\"Quote not found\\\");\\n}\\nconst author = $node[\\\"Merge File Selection Data\\\"].json[\\\"Author\\\"];\\nif (!author) {\\n  throw new Error(\\\"Author not found\\\");\\n}\\n\\n// Split the transcript into words and group them into lines based on maxCharsPerLine\\nconst words = transcript.split(' ');\\nconst lines = [];\\nlet currentLine = \\\"\\\";\\nlet currentCharCount = 0;\\n\\nwords.forEach(word => {\\n  const wordLength = word.length;\\n  const additionalSpace = currentLine ? 1 : 0;\\n  const potentialLength = currentCharCount + additionalSpace + wordLength;\\n  if (potentialLength <= maxCharsPerLine) {\\n    currentLine += (currentLine ? \\\" \\\" : \\\"\\\") + word;\\n    currentCharCount = potentialLength;\\n  } else {\\n    lines.push(currentLine);\\n    currentLine = word;\\n    currentCharCount = wordLength;\\n  }\\n});\\nif (currentLine) {\\n  lines.push(currentLine);\\n}\\n\\n// Calculate layout for the quote block\\nconst lineHeight = quoteFontSize * lineHeightMultiplier;\\nconst totalHeight = lines.length * lineHeight;\\n\\n// Build drawtext commands for each quote line (centered horizontally)\\n// Each line is positioned so that the entire quote block is vertically centered.\\nconst quoteCommands = lines.map((line, index) => {\\n  // Escape any single quotes in the line\\n  const escapedLine = line.replace(/'/g, \\\"\\\\\\\\'\\\");\\n  return `drawtext=fontfile=${quoteFont}:text='${escapedLine}':fontsize=${quoteFontSize}:fontcolor=${fontColor}:x=(w-text_w)/2:y=((h-${totalHeight})/2)+(${index}*${lineHeight})`;\\n});\\n\\n// Build the drawtext command for the author\\n// Place the author text below the quote block with a small gap (e.g. 20 pixels)\\n// Align it to the right by setting x = w - text_w - margin.\\nconst authorY = `((h-${totalHeight})/2)+(${lines.length}*${lineHeight})+20`;\\nconst escapedAuthor = author.replace(/'/g, \\\"\\\\\\\\'\\\");\\nconst authorCommand = `drawtext=fontfile=${authorFont}:text='${escapedAuthor}':fontsize=${authorFontSize}:fontcolor=${fontColor}:x=w-text_w-${margin}:y=${authorY}`;\\n\\n// Combine all commands (separated by commas) into one drawtext filter string.\\nconst fullDrawTextFilter = quoteCommands.concat(authorCommand).join(\\\", \\\");\\n\\n// Return the prepared filter string for insertion into your FFmpeg command.\\nreturn {\\n  json: {\\n    drawText: fullDrawTextFilter\\n  }\\n};\\n\"\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This code node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"082cf794-89a9-42cc-b9ee-96792a17893f\",\n      \"name\": \"Generate Final Video Clip\",\n      \"type\": \"n8n-nodes-base.executeCommand\",\n      \"position\": [\n        1640,\n        340\n      ],\n      \"parameters\": {\n        \"command\": \"=ffmpeg -i {{ $('Save Video Background Locally').item.json.fileName }} -i {{ $('Save Music Background Locally').item.json.fileName }} -filter_complex \\\"[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920[vid]; color=black@0.3:size=1080x1920:d=10[bg]; [vid][bg]overlay=shortest=1[bgvid]; [bgvid]{{ $json.drawText }}[outv]; [1:a]volume=0.8[aout]\\\" -map \\\"[outv]\\\" -map \\\"[aout]\\\" -aspect 9:16 -c:v libx264 -c:a aac -shortest output.mp4 -y\"\n      },\n      \"typeVersion\": 1,\n      \"notes\": \"This executeCommand node performs automated tasks as part of the workflow.\"\n    },","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/Zie619/n8n-workflows/blob/94007c1445d9258a7da116646b79473e7c7c3282/workflows/Code/1864_Code_Executecommand_Create_Webhook.json#L416-L452","documentation":"Same 'Prepare Overlay Text' node as error 50, second guard: the Author field read from $node['Merge File Selection Data'].json is falsy. The quote may have been found, but the attribution line for the FFmpeg overlay is missing, so the node refuses to build a half-labeled video.","triggerScenarios":"The merged quote item has no Author key, or Author is empty string/null. Typical when the quotes source stores quotes without attribution (anonymous quotes), the Author column is blank in the sheet, or the field name drifted ('author', 'Author Name').","commonSituations":"Quotes spreadsheet with optional author column left blank; source API returning quotes without attribution; field renamed upstream.","solutions":["Fill in the Author value in the quote source, or make the source always emit a default like 'Unknown'.","If anonymous quotes are acceptable, default the author instead of throwing: const author = raw || 'Unknown';","Verify the field name/casing in the merge node output matches 'Author' exactly.","Validate both Qoute and Author at the selection stage (error 49's node) so failures surface before FFmpeg rendering."],"exampleFix":"// before\nconst author = $node[\"Merge File Selection Data\"].json[\"Author\"];\nif (!author) {\n  throw new Error(\"Author not found\");\n}\n\n// after (allow anonymous quotes with a fallback label)\nconst merged = $('Merge File Selection Data').first().json;\nconst author = (merged[\"Author\"] ?? '').toString().trim() || 'Unknown';","handlingStrategy":"fallback","validationCode":"const merged = $('Merge File Selection Data').first().json;\nconst author = (merged[\"Author\"] ?? '').toString().trim() || 'Unknown'; // anonymous quotes allowed","typeGuard":"function resolveAuthor(merged) {\n  const a = (merged?.[\"Author\"] ?? merged?.[\"author\"] ?? '').toString().trim();\n  return a.length ? a : null; // null = anonymous\n}","tryCatchPattern":null,"preventionTips":["Decide explicitly whether Author is required; if optional, substitute 'Unknown' instead of throwing.","Default the Author column in the source sheet for anonymous entries.","Keep field names consistent between source, merge, and this reader.","Surface missing-author data as a warning in logs while still rendering the video."],"tags":["n8n","ffmpeg","field-name","optional-field","code-node"],"backgroundTag":null,"analyzedSha":"94007c1445d9258a7da116646b79473e7c7c3282","analyzedAt":"2026-08-15T04:10:37.591Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}