elsa-workflows/elsa-core · error · InvalidOperationException

Only managed secret bindings can remove managed secret…

Error message

Only managed secret bindings can remove managed secret material.

What it means

RemoveAsync refuses to delete secret material referenced by a binding whose Ownership is not SecretBindingOwnership.Managed. This protects externally owned (e.g. configuration-backed read-only) bindings from having their referenced secret material deleted through this resolver.

Solutions

  1. Check binding.Ownership == SecretBindingOwnership.Managed before calling RemoveAsync
  2. Remove the binding from the source that owns it instead of deleting managed material
  3. Skip non-managed bindings in bulk cleanup loops

Example fix

// before
await resolver.RemoveAsync(binding);
// after
if (binding.Ownership == SecretBindingOwnership.Managed)
    await resolver.RemoveAsync(binding);
Defensive patterns

Strategy: type-guard

Validate before calling

if (binding.Ownership != SecretBindingOwnership.Managed) return; // skip: not managed

Type guard

bool CanRemove(SecretBinding b) => b.Ownership == SecretBindingOwnership.Managed;

Try / catch

try { await resolver.RemoveAsync(binding); }
catch (InvalidOperationException) { /* binding not managed; skip or handle elsewhere */ }

Prevention

When it happens

Trigger: Calling RemoveAsync and passing a SecretBinding whose Ownership is System/External instead of Managed; typically after fetching or constructing a binding without checking its Ownership.

Common situations: Cleanup scripts that iterate all bindings and attempt removal without filtering by ownership; code paths that assume every binding the resolver hands out is managed.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/424e79443eadb08b. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication.Secrets/Services/ElsaSecretBindingResolver.cs:48

        // so a stale request can never rotate material used by the live binding.
        var name = $"external-authentication:{Guid.NewGuid():N}";
        var secret = await secretManager.CreateAsync(new()
        {
            Name = name,
            DisplayName = $"External authentication {request.FieldName}",
            TypeName = SecretTypeNames.Text,
            StoreName = SecretStoreNames.Encrypted,
            Value = request.Value.Reveal()
        }, cancellationToken);

        return new(ResolverType, secret.Name, Ownership: SecretBindingOwnership.Managed);
    }

    public async ValueTask RemoveAsync(SecretBinding binding, CancellationToken cancellationToken = default)
    {
        EnsureResolverType(binding);
        if (binding.Ownership != SecretBindingOwnership.Managed)
            throw new InvalidOperationException("Only managed secret bindings can remove managed secret material.");
        await secretManager.DeleteAsync(binding.Reference, cancellationToken);
    }

    public async ValueTask<SecretBindingState> GetStateAsync(SecretBinding binding, CancellationToken cancellationToken = default)
    {
        EnsureResolverType(binding);
        var secret = await secretManager.GetAsync(binding.Reference, cancellationToken);
        if (secret is null)
            return new(false, false);

        var configured = secret is { Status: SecretStatus.Active, LatestActiveVersion: not null };
        if (!configured || !IsCompatible(secret, binding))
            return new(configured, false);

        var test = await secretManager.TestAsync(secret.Name, cancellationToken);
        return new(true, test.Succeeded);
    }

View on GitHub (pinned to fe9217bdfa)