elsa-workflows/elsa-core · error · BpmnInterchangeException

The document declares

Error message

The document declares {definitions.Processes.Count} processes ({declared}); specify which one to import.

What it means

BpmnInterchangeDocumentService refuses to import a BPMN document when more than one process is declared at the root and no specific process was selected. BPMN interchange files can contain multiple <process> definitions, so the importer needs an unambiguous target. It throws BpmnInterchangeException naming the declared process ids.

Solutions

  1. Specify which process to import by passing its process id to the import API.
  2. If the file should contain one process, split the document so each file declares a single <process>.
  3. Verify the document is not empty/corrupt — 0 declared processes also triggers this throw.
  4. Check the declared ids in the error message and confirm the one you intended exists in the file.

Example fix

// before
var definition = await documentService.ImportDocumentAsync(stream);
// after
var definition = await documentService.ImportDocumentAsync(stream, processId: "OrderProcess");
Defensive patterns

Strategy: validation

Validate before calling

var processCount = doc.Definitions?.Processes?.Count ?? 0;
if (processCount != 1) throw new ArgumentException($"Document must declare exactly one process (found {processCount}); specify the process id to import.");

Type guard

bool IsSingleProcessDocument(BpmnDefinitions d) => d?.Processes?.Count == 1;

Try / catch

try { var def = await svc.ImportDocumentAsync(stream, processId); }
catch (BpmnInterchangeException ex) { log.LogWarning(ex, "Ambiguous BPMN document"); /* prompt user to pick a process */ }

Prevention

When it happens

Trigger: Calling the import/root-definition resolution (ResolveRootDefinition) on a BPMN document whose definitions element declares 0 or 2+ processes, without specifying which process to import (e.g. rootDefinition without a process id selector).

Common situations: Exporting a multi-process diagram from a modeling tool (Camunda, Signavio) and importing it wholesale; merging several diagrams into one file; accidentally passing an empty or corrupted definitions file.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs:856

        return xml;
    }

    private static BpmnProcessDefinition ResolveRootDefinition(BpmnDefinitions definitions, string? processId)
    {
        if (!string.IsNullOrWhiteSpace(processId))
        {
            // BpmnImportOptions.ProcessId already made the read fail fast if the document does not declare this
            // process, so finding it here can only fail if that guarantee itself changes.
            return definitions.Processes.First(process => string.Equals(process.ProcessId, processId, StringComparison.Ordinal));
        }

        if (definitions.Processes.Count == 1)
            return definitions.Processes[0];

        var declared = string.Join(", ", definitions.Processes.Select(process => process.ProcessId));

        throw new BpmnInterchangeException(
            $"The document declares {definitions.Processes.Count} processes ({declared}); specify which one to import.");
    }

    /// <summary>
    /// Refuses a document that declares the same element id more than once, naming the duplicated ids.
    /// </summary>
    /// <remarks>
    /// <para>
    /// BPMN requires every element id to be unique within a document. The library's own reader tolerates a repeat —
    /// <c>BpmnXmlReader</c> walks the XML's own element tree, so it terminates regardless of what any id says — but
    /// nothing downstream of it does: this type's own <c>EnsureCapabilitiesSatisfied</c> and <c>BpmnWorkBinder.BindScope</c>
    /// both find "the nested processes belonging to this scope" by matching <see cref="BpmnWorkBinding.ProcessId"/>
    /// against the scope's own id, and <c>Bpmn.Interchange</c>'s own <c>BpmnXmlWriter</c> does the same by matching
    /// <see cref="BpmnWorkBinding.BindingRef"/>. A <see cref="BpmnWorkBinding.NestedProcess"/>'s own
    /// <see cref="BpmnProcessDefinition.ProcessId"/> is always the element id of the subprocess element that opens
    /// it, so a subprocess nested inside another subprocess that reuses its parent's id makes that lookup find its
    /// own parent — or itself — again on every step down. Each of those three walks then recurses without ever
    /// terminating and crashes the process outright: .NET cannot catch a <see cref="StackOverflowException"/>. This

View on GitHub (pinned to fe9217bdfa)