Skip to content

fix(workflows): keep the version history tab alive without a cascading workspace - #994

Open
RalfvandenBurg wants to merge 1 commit into
elsa-workflows:mainfrom
RalfvandenBurg:fix/version-history-tab-missing-workspace
Open

fix(workflows): keep the version history tab alive without a cascading workspace#994
RalfvandenBurg wants to merge 1 commit into
elsa-workflows:mainfrom
RalfvandenBurg:fix/version-history-tab-missing-workspace

Conversation

@RalfvandenBurg

Copy link
Copy Markdown
Contributor

The problem

VersionHistoryTab takes its workspace as a [CascadingParameter] of the concrete WorkflowDefinitionWorkspace type:

[CascadingParameter] private WorkflowDefinitionWorkspace Workspace { get; set; } = default!;

protected override void OnInitialized() => Workspace.WorkflowDefinitionUpdated += OnWorkflowDefinitionUpdated;
void IDisposable.Dispose()              => Workspace.WorkflowDefinitionUpdated -= OnWorkflowDefinitionUpdated;

Any host that cascades a workspace of a different type leaves that parameter null. Downstream hosts reach this state by copying WorkflowDefinitionWorkspace into their own namespace to change which WorkflowEditor it instantiates: the copy still does <CascadingValue Value="this"> and still renders this package's un-forked WorkflowProperties, but the cascaded value no longer matches the type this tab asks for.

Because both dereferences were unconditional, opening the tab produced two exceptions:

[Warning] ErrorBoundary   Unhandled exception rendering component: Object reference not set to an instance of an object.
   at ...Tabs.VersionHistory.VersionHistoryTab.OnInitialized()

[Warning] RemoteRenderer  Unhandled exception rendering component: Object reference not set to an instance of an object.
   at ...Tabs.VersionHistory.VersionHistoryTab.System.IDisposable.Dispose()
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.DisposeInBatchAsync(RenderBatchBuilder)

[Error]   CircuitHost     Unhandled exception in circuit '...'

The first is caught by an error boundary. The second is thrown from Dispose() inside the renderer's disposal batch, where no boundary can catch it — so it terminates the entire Blazor circuit. The user sees An error has occurred. This application may no longer respond until reloaded. and every other page in the Studio is dead until they reload, not just this tab.

Version history is the only one of the four workflow property tabs that reaches for the workspace this way — Properties, Variables and Input/Output take the definition as an ordinary parameter — which is why nothing else broke.

The change

Workspace becomes nullable and every use tolerates its absence:

  • OnInitialized / Dispose — subscribe and unsubscribe only when a workspace is present, so nothing can throw during the disposal batch.
  • IsReadOnlytrue and HasWorkflowEditPermissionfalse when there is no workspace, which disables bulk actions, rollback and delete through the existing CanRollback / CanDelete checks.
  • ViewVersionAsync, OnRollbackClicked, OnDeleteClicked, OnBulkDeleteClicked — return early rather than dereference.

The version list itself still loads and is browsable, since it only needs DefinitionId and IWorkflowDefinitionService. The one behaviour given up is the auto-refresh subscription, and only in hosts where the parameter was already null.

This is deliberately the conservative fix. The underlying design question — that this tab depends on a concrete component type while its siblings depend on IWorkspace, which today carries only IsReadOnly and HasWorkflowEditPermission — would need IWorkspace widened with WorkflowDefinitionUpdated, DisplayWorkflowDefinitionVersionAsync, DisplayLatestWorkflowDefinitionVersionAsync, IsSelectedDefinition and GetSelectedDefinition. Happy to follow up with that if you would prefer it over the guards.

Tests

VersionHistoryTabMissingWorkspaceTests renders the tab with no cascading workspace and pins three things: the version list loads, disposal does not throw, and the edit actions are disabled. All three fail on main with NullReferenceException at VersionHistoryTab.OnInitialized() and pass with this change.

Elsa.Studio.Workflows.Tests: 210/210 pass. Elsa.Studio.Workflows builds with 0 warnings.

🤖 Generated with Claude Code

…g workspace

VersionHistoryTab takes its workspace as a [CascadingParameter] of the concrete
WorkflowDefinitionWorkspace type, so a host that cascades a workspace of any
other type leaves the parameter null. Downstream hosts hit this by copying
WorkflowDefinitionWorkspace into their own namespace to change what it
instantiates: the copy still cascades itself and still renders this package's
un-forked WorkflowProperties, but the cascaded value no longer matches the type
this tab asks for.

The tab then dereferenced Workspace unconditionally. OnInitialized threw a
NullReferenceException into an error boundary, and IDisposable.Dispose threw a
second one from the disposal batch of the renderer, where no boundary can catch
it - so opening the tab terminated the whole Blazor circuit and every other page
in the Studio went dead behind "An error has occurred. This application may no
longer respond until reloaded."

Version history is the only one of the four workflow property tabs that reaches
for the workspace this way; Properties, Variables and Input/Output take the
definition as an ordinary parameter, which is why nothing else broke.

Workspace is now nullable and every use tolerates its absence: the version list
still loads and is browsable, while the actions that genuinely need a workspace
(view, rollback, delete, bulk delete) report no edit permission and return
early. Loss of the auto-refresh subscription is the only behaviour given up, and
only in hosts where the parameter was already null.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

PR author is not in the allowed authors list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants