@@ -14,6 +14,17 @@ declare
1414 v_dependent_map_slug text ;
1515begin
1616
17+ -- ==========================================
18+ -- GUARD: No mutations on failed runs
19+ -- ==========================================
20+ IF EXISTS (SELECT 1 FROM pgflow .runs WHERE pgflow .runs .run_id = complete_task .run_id AND pgflow .runs .status = ' failed' ) THEN
21+ RETURN QUERY SELECT * FROM pgflow .step_tasks
22+ WHERE pgflow .step_tasks .run_id = complete_task .run_id
23+ AND pgflow .step_tasks .step_slug = complete_task .step_slug
24+ AND pgflow .step_tasks .task_index = complete_task .task_index ;
25+ RETURN;
26+ END IF;
27+
1728-- ==========================================
1829-- VALIDATION: Array output for dependent maps
1930-- ==========================================
@@ -37,11 +48,55 @@ WHERE dependency.dep_slug = complete_task.step_slug -- parent is the completing
3748LIMIT 1 ;
3849
3950IF v_dependent_map_slug IS NOT NULL THEN
40- RAISE EXCEPTION ' Map step % expects array input but dependency % produced % (output: %)' ,
41- v_dependent_map_slug,
42- complete_task .step_slug ,
43- CASE WHEN complete_task .output IS NULL THEN ' null' ELSE jsonb_typeof(complete_task .output ) END,
44- complete_task .output ;
51+ -- Mark run as failed immediately
52+ UPDATE pgflow .runs
53+ SET status = ' failed' ,
54+ failed_at = now()
55+ WHERE pgflow .runs .run_id = complete_task .run_id ;
56+
57+ -- Archive all queued messages
58+ PERFORM pgmq .archive (r .flow_slug , st .message_id )
59+ FROM pgflow .step_tasks st
60+ JOIN pgflow .runs r ON st .run_id = r .run_id
61+ WHERE st .run_id = complete_task .run_id
62+ AND st .status = ' queued'
63+ AND st .message_id IS NOT NULL ;
64+
65+ -- Mark current task as failed
66+ UPDATE pgflow .step_tasks
67+ SET status = ' failed' ,
68+ failed_at = now(),
69+ error_message = ' [TYPE_VIOLATION] Produced ' ||
70+ CASE WHEN complete_task .output IS NULL THEN ' null'
71+ ELSE jsonb_typeof(complete_task .output ) END ||
72+ ' instead of array'
73+ WHERE pgflow .step_tasks .run_id = complete_task .run_id
74+ AND pgflow .step_tasks .step_slug = complete_task .step_slug
75+ AND pgflow .step_tasks .task_index = complete_task .task_index ;
76+
77+ -- Mark step state as failed
78+ UPDATE pgflow .step_states
79+ SET status = ' failed' ,
80+ failed_at = now(),
81+ error_message = ' [TYPE_VIOLATION] Map step ' || v_dependent_map_slug ||
82+ ' expects array input but dependency ' || complete_task .step_slug ||
83+ ' produced ' || CASE WHEN complete_task .output IS NULL THEN ' null'
84+ ELSE jsonb_typeof(complete_task .output ) END
85+ WHERE pgflow .step_states .run_id = complete_task .run_id
86+ AND pgflow .step_states .step_slug = complete_task .step_slug ;
87+
88+ -- Archive the current task's message (it was started, now failed)
89+ PERFORM pgmq .archive (r .flow_slug , st .message_id )
90+ FROM pgflow .step_tasks st
91+ JOIN pgflow .runs r ON st .run_id = r .run_id
92+ WHERE st .run_id = complete_task .run_id
93+ AND st .step_slug = complete_task .step_slug
94+ AND st .task_index = complete_task .task_index
95+ AND st .message_id IS NOT NULL ;
96+
97+ -- Return empty result
98+ RETURN QUERY SELECT * FROM pgflow .step_tasks WHERE false;
99+ RETURN;
45100END IF;
46101
47102-- ==========================================
0 commit comments