elsa-workflows/elsa-core · error · HubException
Access denied.
Error message
Access denied.
What it means
WorkflowInstanceHub.ObserveInstanceAsync first checks whether the connected client is authorized to read workflow instances (CanReadWorkflowInstances). If that permission check fails, the hub throws HubException("Access denied.") before touching the store, and the client's SignalR invocation fails.
Solutions
- Ensure the SignalR connection is authenticated with a valid token/cookie and the user has the workflow-instance read permission/policy.
- Fix the Elsa authorization configuration (policies/claims/roles) so intended users can read workflow instances.
- Handle the HubException on the client and prompt the user to re-authenticate or request access.
Example fix
// before
connection = new HubConnectionBuilder().WithUrl("https://host/hubs/workflow-instance").Build(); // no token
// after
connection = new HubConnectionBuilder().WithUrl("https://host/hubs/workflow-instance", opts =>
opts.AccessTokenProvider = () => Task.FromResult(authToken)).Build(); Defensive patterns
Strategy: type-guard
Validate before calling
var permitted = user.HasClaim("permissions", "WorkflowInstances:Read");
if (!permitted) throw new UnauthorizedAccessException("User cannot observe workflow instances."); Type guard
var mayObserve = hubContextUser?.Identity?.IsAuthenticated == true && user.Claims.Any(c => c.Type == "permission" && c.Value.Contains("workflow-instance")); Try / catch
try { await connection.InvokeAsync("ObserveInstanceAsync", instanceId); }
catch (HubException ex) when (ex.Message == "Access denied.") { /* redirect to login / request permissions */ } Prevention
- Gate hub subscriptions in the UI based on the user's permissions
- Pass access tokens via AccessTokenProvider when building the hub connection
- Test hub authorization with unauthenticated and under-privileged users
When it happens
Trigger: A SignalR client invokes ObserveInstanceAsync while the connection's user lacks the permission/claim/policy required to read workflow instances — e.g. an unauthenticated connection, or a user whose role does not grant workflow-instance read access.
Common situations: Connecting to the hub without an auth cookie/token; tokens lacking the workflow-read claim after a role change; misconfigured Elsa authorization policies that deny the read permission by default; forgetting to configure authentication middleware for the hub path.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied.
- Access denied.
- ' ' is not a well-formed permission. Expected ' : '.
- Cannot overwrite an AI conversation that belongs to another…
- Cannot overwrite an AI conversation that belongs to another…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/e00729c58eff19fc.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs:40
private static readonly Permission ReadInstances = new(Elsa.Workflows.Api.Permissions.WorkflowPermissions.Instances, CoreVerbs.View);
private readonly IWorkflowInstanceStore _workflowInstanceStore;
private readonly ITenantAccessor? _tenantAccessor;
/// <inheritdoc />
public WorkflowInstanceHub(IWorkflowInstanceStore workflowInstanceStore, ITenantAccessor? tenantAccessor = null)
{
_workflowInstanceStore = workflowInstanceStore;
_tenantAccessor = tenantAccessor;
}
/// <summary>
/// Observes a workflow instance.
/// </summary>
/// <param name="instanceId">The ID of the workflow instance to observe.</param>
public async Task ObserveInstanceAsync(string instanceId)
{
if (!CanReadWorkflowInstances())
throw new HubException("Access denied.");
var workflowInstance = await _workflowInstanceStore.FindAsync(new WorkflowInstanceFilter { Id = instanceId }, Context.ConnectionAborted);
if (!CanAccessTenant(workflowInstance, _tenantAccessor))
throw new HubException("Access denied.");
// Join the user to the workflow instance group.
await Groups.AddToGroupAsync(Context.ConnectionId, instanceId, Context.ConnectionAborted);
}
private bool CanReadWorkflowInstances()
{
var user = Context.User;
if (user?.Identity?.IsAuthenticated != true)
return false;
return PermissionEvaluator.Shared.HasPermission(user, ReadInstances);View on GitHub (pinned to fe9217bdfa)