Skip to content

Commit a62dd79

Browse files
committed
fix(workflows): say when a block is dropped before persistence
`workflow_blocks.name` is NOT NULL, so a block missing `type` or `name` has to be dropped — but it was dropped silently. A block with no edges left no trace anywhere: not in the returned warnings, not in a log line. The client sanitizer warns on the identical condition; this is its server counterpart, and the warnings array it feeds is already returned by the internal PUT, the v2 write and the importer.
1 parent 0a5bc43 commit a62dd79

2 files changed

Lines changed: 20 additions & 5 deletions

File tree

apps/sim/lib/workflows/persistence/prepare-state.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ describe('prepareWorkflowStateForPersistence', () => {
3636
expect(warnings.some((w) => w.includes('dangling'))).toBe(true)
3737
})
3838

39-
it('drops blocks missing type or name', () => {
40-
const { state } = prepareWorkflowStateForPersistence({
39+
it('drops blocks missing type or name, and says which', () => {
40+
const { state, warnings } = prepareWorkflowStateForPersistence({
4141
blocks: {
4242
ok: block({ id: 'ok' }),
4343
noType: block({ id: 'noType', type: '' }),
@@ -47,6 +47,9 @@ describe('prepareWorkflowStateForPersistence', () => {
4747
})
4848

4949
expect(Object.keys(state.blocks)).toEqual(['ok'])
50+
// A block with no edges left no other trace of having been dropped.
51+
expect(warnings).toContain('Dropped block "noType": missing type or name')
52+
expect(warnings).toContain('Dropped block "noName": missing type or name')
5053
})
5154

5255
it('backfills the columns the normalized tables require', () => {

apps/sim/lib/workflows/persistence/prepare-state.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ export interface PrepareWorkflowStateResult {
2525
*
2626
* The steps are order-dependent:
2727
* 1. Strip secrets from inline agent-tool definitions.
28-
* 2. Drop blocks missing `type`/`name` and backfill the columns the tables
29-
* require, so a partial block cannot violate a NOT NULL constraint.
28+
* 2. Drop blocks missing `type`/`name` — reporting each one — and backfill the
29+
* columns the tables require, so a partial block cannot violate a NOT NULL
30+
* constraint.
3031
* 3. Drop edges whose endpoints no longer resolve — `workflow_edges` has
3132
* foreign keys onto `workflow_blocks`, so a dangling edge would otherwise
3233
* abort the whole transaction with an opaque database error.
@@ -42,8 +43,18 @@ export function prepareWorkflowStateForPersistence(state: {
4243
)
4344

4445
const blocks: Record<string, BlockState> = {}
46+
const droppedBlockWarnings: string[] = []
4547
for (const [blockId, block] of Object.entries(sanitizedBlocks)) {
46-
if (!block.type || !block.name) continue
48+
/**
49+
* Reported, not just skipped: `workflow_blocks.name` is NOT NULL so the
50+
* drop has to happen, but a block with no edges left no trace anywhere and
51+
* simply vanished from the saved workflow. The client-side sanitizer warns
52+
* on the identical condition; this is its server-side counterpart.
53+
*/
54+
if (!block.type || !block.name) {
55+
droppedBlockWarnings.push(`Dropped block "${blockId}": missing type or name`)
56+
continue
57+
}
4758
blocks[blockId] = {
4859
...block,
4960
enabled: block.enabled !== undefined ? block.enabled : true,
@@ -65,6 +76,7 @@ export function prepareWorkflowStateForPersistence(state: {
6576
},
6677
warnings: [
6778
...sanitizationWarnings,
79+
...droppedBlockWarnings,
6880
...validatedEdges.dropped.map(({ edge, reason }) => `Dropped edge "${edge.id}": ${reason}`),
6981
],
7082
}

0 commit comments

Comments
 (0)