Skip to content

[pull] main from czlonkowski:main - #61

Merged
pull[bot] merged 1 commit into
5474312:mainfrom
czlonkowski:main
May 4, 2026
Merged

[pull] main from czlonkowski:main#61
pull[bot] merged 1 commit into
5474312:mainfrom
czlonkowski:main

Conversation

@pull

@pull pull Bot commented May 4, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

…save (#769)

* fix(workflow-diff): roll back to prior state when n8n PUT fails post-save

When n8n_update_partial_workflow's underlying PUT fails (e.g. n8n persisted
the workflow body but the activation step within the same PUT then tripped
on an unsupported typeVersion), the workflow was being left in a broken
state. The handler already snapshots the prior workflow into workflowBefore
for telemetry, so we piggyback on it: on PUT failure, attempt a rollback
PUT to restore the prior state.

Three return paths on failure:
- Pre-mutation (no snapshot, e.g. validateOnly) — preserve current behaviour
- Rollback succeeded — error suffixed with "(workflow restored to prior
  state)" + details.rollbackPerformed = true
- Rollback failed — composite error pointing to n8n_workflow_versions for
  manual recovery + details.rollbackPerformed = false + rollbackError

Success path is byte-identical to before.

Tests:
- New "should roll back to prior state when n8n PUT fails after persisting
  body" test asserts the rollback PUT fires with the prior snapshot
- New "should report rollback failure when both PUTs fail" test asserts
  the composite error is surfaced
- Existing "should not attempt rollback in validateOnly mode" remains intact

Known limitations (documented in PR description):
- Concurrent writers can race the rollback. Same race window as today's
  plain PUT. A future iteration could use versionId for optimistic
  concurrency.
- Rollback fires even when n8n rejected pre-save (body never persisted).
  Rollback is a no-op in that case — one extra HTTP request on the
  failure path. Chose this over guessing n8n's error semantics.

* fix(workflow-diff): skip rollback on pre-save rejection + actionable recovery

Building on the rollback-on-error fix, distinguish two failure modes via a
post-failure GET that compares versionId / versionCounter / updatedAt
against the snapshot:

- Persist-then-fail (n8n saved the body before activation died): roll back
  by re-PUTting the prior snapshot. Error gets the existing
  "(workflow restored to prior state)" suffix.
- Pre-save rejection (body never persisted): skip the rollback PUT — it
  would be wasted and the suffix would mislead the caller.
  details.rollbackPerformed: false; no suffix.

When rollback fails, surface workflowBefore.versionId as
details.priorVersionId so callers can recover via n8n_workflow_versions.

The version comparison is tri-state (same / changed / unknown). On
"unknown" — older n8n versions that omit all three fields, or a failing
post-failure GET — fall back to attempting rollback. The silent-corruption
class from #770 is far worse than a redundant PUT.

Tests cover persist-then-fail rollback, pre-save no-rollback, GET-failure
best-effort rollback, versionCounter-only fallback, no-version-fields
safety net, and double-PUT-failure with priorVersionId. 45/45 in
handlers-workflow-diff.test.ts; 611/611 across tests/unit/mcp.

Bumped to v2.50.3.
@pull pull Bot locked and limited conversation to collaborators May 4, 2026
@pull pull Bot added the ⤵️ pull label May 4, 2026
@pull
pull Bot merged commit fe3a41f into 5474312:main May 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant