Zie619/n8n-workflows · error · Error
Quote not found
Error message
Quote not found
What it means
Thrown by 'Prepare Overlay Text (Quote & Author)1' when $('Get data from Google Sheet').first().json['Quote (Thai)'] is falsy. The node reads the Thai quote and pen name from the first item of the Google Sheets node and builds an ffmpeg drawtext filter (word-wrapping, fonts, layout) for a video overlay. A missing column value — renamed column, empty cell, or empty sheet — triggers this.
Source
Thrown at workflows/Wait/1400_Wait_Code_Automation_Webhook.json:822
"options": {
"append": false
},
"fileName": "=SoundBackground.mp3",
"operation": "write"
},
"typeVersion": 1,
"notes": "This readWriteFile node performs automated tasks as part of the workflow."
},
{
"id": "b9332740-c4e0-40f9-bc0d-c550c8f0f96d",
"name": "Prepare Overlay Text (Quote & Author)1",
"type": "n8n-nodes-base.code",
"position": [
300,
860
],
"parameters": {
"jsCode": "// Define separate configuration for the quote and the author\nconst quoteFont = \"Kanit-Italic.ttf\"; \nconst quoteFontSize = 70;\nconst authorFont = \"Kanit-Italic.ttf\"; \nconst authorFontSize = 50;\nconst fontColor = \"white\";\nconst lineHeightMultiplier = 1.1;\nconst videoWidth = 1080;\nconst margin = 40; \n\n// Effective width for the quote text\nconst effectiveVideoWidth = videoWidth - 2 * margin;\n\n// Estimate average character width based on quoteFontSize\nconst avgCharWidth = quoteFontSize * 0.6;\nconst maxCharsPerLine = Math.floor(effectiveVideoWidth / avgCharWidth);\n\n// Retrieve the quote and author from \"Select Random Video, Music & Quote\"\nconst transcript = $('Get data from Google Sheet').first().json['Quote (Thai)'];\nif (!transcript) {\n throw new Error(\"Quote not found\");\n}\n\nconst author = $('Get data from Google Sheet').first().json['Pen Name (Thai)'];\nif (!author) {\n throw new Error(\"Author not found\");\n}\n\n// Split transcript into words and group them into lines\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 quote lines\nconst quoteCommands = lines.map((line, index) => {\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 author\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 into one drawtext filter string\nconst fullDrawTextFilter = quoteCommands.concat(authorCommand).join(\", \");\n\n// Return the prepared filter string\nreturn {\n json: {\n drawText: fullDrawTextFilter\n }\n};"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},
{
"id": "79764093-f6ca-459b-a73c-3326fe82fafa",
"name": "Generate Final Video Clip1",
"type": "n8n-nodes-base.executeCommand",
"position": [
480,
860
],
"parameters": {
"command": "=ffmpeg -i {{ $('Save Video Background Locally1').item.json.fileName }} -i {{ $('Save Music Background Locally1').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"
},
"typeVersion": 1,
"notes": "This executeCommand node performs automated tasks as part of the workflow."
},View on GitHub (pinned to 94007c1445)
Solutions
- Pin the output of 'Get data from Google Sheet' and check the exact keys of item 0 (spacing/casing of 'Quote (Thai)').
- Fix the sheet: restore the header, fill the empty cell, or update the Sheets node column selection to include 'Quote (Thai)' and 'Pen Name (Thai)'.
- Filter rows upstream so only rows with a non-empty quote can reach this node.
- Fail with context: include Object.keys(row) in the error message to make the next occurrence self-diagnosing.
Example fix
// before
const transcript = $('Get data from Google Sheet').first().json['Quote (Thai)'];
if (!transcript) {
throw new Error("Quote not found");
}
// after
const sheetRow = $('Get data from Google Sheet').first().json;
const transcript = sheetRow['Quote (Thai)'];
if (!transcript || !String(transcript).trim()) {
throw new Error(`Quote not found. Available columns: ${Object.keys(sheetRow).join(', ')}`);
} Defensive patterns
Strategy: validation
Validate before calling
const row = $('Get data from Google Sheet').first().json;
const quote = row['Quote (Thai)'];
const author = row['Pen Name (Thai)'];
if (!quote || !String(quote).trim() || !author) {
throw new Error(`Quote/author missing. Sheet columns: ${Object.keys(row).join(', ')}`);
} Type guard
const hasQuoteRow = (row) => typeof row?.['Quote (Thai)'] === 'string' && row['Quote (Thai)'].trim() !== '' && typeof row?.['Pen Name (Thai)'] === 'string' && row['Pen Name (Thai)'].trim() !== '';
Prevention
- Filter the sheet rows upstream so only rows with non-empty 'Quote (Thai)' can be selected.
- Protect the header row and column names in shared Google Sheets (locked ranges/data validation).
- Include the available column names in the error so a renamed header is diagnosed in one look.
When it happens
Trigger: The sheet column was renamed from 'Quote (Thai)' (extra space, different casing, English name), the selected row has an empty quote cell, the sheet lookup returned zero matching rows (first() then yields an empty json), or the Sheets node's 'Which Sheet/Columns' config drifted from the live sheet.
Common situations: Shared Google Sheets edited by humans (renamed headers, cleared cells), a filter/random-select step picking an incomplete row, or the Sheets node configured to return only certain columns so 'Quote (Thai)' is absent from json.
Related errors
- The video ID parameter is empty.
- Insufficient stock for ${$('Retrieve Issue Request Details')
- Product ID is missing
- Submission ID is missing
- Approved quantity must be greater than 0
AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15).
Data as JSON: /api/errors/5ed23b17551406fa.
Report an issue: GitHub.