From ccd6519512dd515d7e1559c7c247d333097cdbf3 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Thu, 19 Feb 2026 08:01:33 +1300 Subject: [PATCH] fix: add Gemini prompt fallback from dynamicInputs, improve parallel batch error logging - Resolve prompt from dynamicInputs.prompt in Gemini code path as fallback - Return clear 400 error when prompt is provided but is not a string (corrupted data) - Improve parallel batch error logging to include nodeId and nodeType for diagnosis - Replace find() with for-loop to preserve index mapping between results and batch nodes --- src/app/api/generate/route.ts | 19 ++++++++++++++++++- src/store/workflowStore.ts | 30 +++++++++++++++--------------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/app/api/generate/route.ts b/src/app/api/generate/route.ts index 99659718..3905b404 100644 --- a/src/app/api/generate/route.ts +++ b/src/app/api/generate/route.ts @@ -432,10 +432,27 @@ export async function POST(request: NextRequest) { // Use selectedModel.modelId if available (new format), fallback to legacy model field const geminiModel = (selectedModel?.modelId as ModelType) || model; + // Resolve prompt: use top-level prompt, fall back to dynamicInputs.prompt + // This handles cases where the prompt arrives via dynamicInputs instead of top-level + let resolvedPrompt = prompt; + if (!resolvedPrompt && dynamicInputs?.prompt) { + resolvedPrompt = Array.isArray(dynamicInputs.prompt) + ? dynamicInputs.prompt[0] + : dynamicInputs.prompt; + } + // Validate: if a prompt was provided but isn't a string (corrupted data), return clear error + // If no prompt provided but images exist, that's valid (image-to-image) + if (resolvedPrompt !== undefined && resolvedPrompt !== null && typeof resolvedPrompt !== 'string') { + return NextResponse.json( + { success: false, error: "prompt must be a string" }, + { status: 400 } + ); + } + return await generateWithGemini( requestId, geminiApiKey, - prompt, + resolvedPrompt, images || [], geminiModel, aspectRatio, diff --git a/src/store/workflowStore.ts b/src/store/workflowStore.ts index d255a9f7..ca6917f7 100644 --- a/src/store/workflowStore.ts +++ b/src/store/workflowStore.ts @@ -962,21 +962,21 @@ export const useWorkflowStore = create((set, get) => ({ batch.map((node) => executeSingleNode(node, abortController.signal)) ); - // Check for failures (fail-fast behavior) - const failed = results.find( - (r): r is PromiseRejectedResult => - r.status === 'rejected' && - !(r.reason instanceof DOMException && r.reason.name === 'AbortError') - ); - - if (failed) { - // Log the failure and abort remaining executions - logger.error('workflow.error', 'Node execution failed in parallel batch', { - level: levelIdx, - error: failed.reason instanceof Error ? failed.reason.message : String(failed.reason), - }); - abortController.abort(); - throw failed.reason; + // Check for failures with node context (fail-fast behavior) + for (let i = 0; i < results.length; i++) { + const r = results[i]; + if (r.status === 'rejected' && + !(r.reason instanceof DOMException && r.reason.name === 'AbortError')) { + const failedNode = batch[i]; + logger.error('workflow.error', 'Node execution failed in parallel batch', { + level: levelIdx, + nodeId: failedNode.id, + nodeType: failedNode.type, + error: r.reason instanceof Error ? r.reason.message : String(r.reason), + }); + abortController.abort(); + throw r.reason; + } } } }