{"record":{"id":"639ffd02a6eb5be4","repo":"elsa-workflows/elsa-core","slug":"bpmnerrorcodes-exportsourceversionunknown","errorCode":"BpmnErrorCodes.ExportSourceVersionUnknown","errorMessage":"Workflow definition '{definition.DefinitionId}' carries BPMN source, but not the definition version it was recorded against, so whether that source still matches this definition cannot be verified. It does not mean this definition was never imported from BPMN, and it does not mean the source is stale — there is simply no version recorded to compare against. Re-import the document to record a complete, exportable source.","messagePattern":"Workflow definition '(.+?)' carries BPMN source, but not the definition version it was recorded against, so whether that source still matches this definition cannot be verified\\. It does not mean this definition was never imported from BPMN, and it does not mean the source is stale — there is simply no version recorded to compare against\\. Re-import the document to record a complete, exportable source\\.","errorType":"exception","errorClass":"BpmnExportUnavailableException","httpStatus":422,"severity":"error","filePath":"src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs","lineNumber":805,"sourceCode":"    {\n        if (!definition.CustomProperties.TryGetValue<string>(SourceXmlCustomPropertyKey, out var xml) || string.IsNullOrEmpty(xml))\n        {\n            throw new BpmnExportUnavailableException(\n                $\"Workflow definition '{definition.DefinitionId}' does not currently carry BPMN source, so it cannot be exported as BPMN 2.0 XML. \"\n                + \"Either it was never imported from a BPMN document, or a later save replaced its custom properties wholesale and removed the \"\n                + $\"'{SourceXmlCustomPropertyKey}' entry as a side effect of editing something else.\",\n                BpmnExportUnavailableReason.NotImported);\n        }\n\n        if (!definition.CustomProperties.TryGetValue<int>(SourceVersionCustomPropertyKey, out var sourceVersion))\n        {\n            // Distinct from both other refusals: this is not \"never imported\" (the source text is right there) and\n            // not \"stale\" (there is no version to compare against yet). ImportAsync writes SourceXmlCustomPropertyKey\n            // and SourceVersionCustomPropertyKey together, in the single save described in its remarks, so this path\n            // is not reachable through import itself; it is kept as a defence against the same combination arising\n            // some other way — e.g. custom properties edited or migrated directly, outside ImportAsync — where\n            // \"whether the source still matches\" cannot be verified without a version to compare against.\n            throw new BpmnExportUnavailableException(\n                $\"Workflow definition '{definition.DefinitionId}' carries BPMN source, but not the definition version it was recorded against, so \"\n                + \"whether that source still matches this definition cannot be verified. It does not mean this definition was never imported from \"\n                + \"BPMN, and it does not mean the source is stale — there is simply no version recorded to compare against. Re-import the document to \"\n                + \"record a complete, exportable source.\",\n                BpmnExportUnavailableReason.SourceVersionUnknown);\n        }\n\n        // The graph hash, once recorded, is the sole word on staleness: it is unaffected by a metadata-only save\n        // (a rename, a variable change) that bumps the definition to a new draft version without touching the graph\n        // the stored source describes, which the version check below would otherwise flag as stale even though the\n        // document still matches exactly. A definition imported before this marker existed carries no value for it,\n        // so it falls back to the version check instead of refusing every definition imported under the older\n        // behaviour.\n        if (definition.CustomProperties.TryGetValue<string>(SourceGraphHashCustomPropertyKey, out var sourceGraphHash) && !string.IsNullOrEmpty(sourceGraphHash))\n        {\n            if (sourceGraphHash != BpmnContentHash.OfGraph(definition.StringData))\n            {\n                throw new BpmnExportUnavailableException(","sourceCodeStart":787,"sourceCodeEnd":823,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs#L787-L823","documentation":"ResolveSourceXml throws BpmnExportUnavailableException (reason SourceVersionUnknown) when the definition carries BPMN source XML but no SourceVersionCustomPropertyKey, so the source cannot be verified against the current definition version. ImportAsync always writes source and version together, so this indicates the custom properties were edited or migrated outside ImportAsync. It is deliberately not treated as 'stale' or 'never imported' — the information to decide is simply absent.","triggerScenarios":"Export encounters CustomProperties with a non-empty SourceXmlCustomPropertyKey but a missing/empty SourceVersionCustomPropertyKey — only reachable by direct custom-property edits, manual data migrations, or scripts modifying stored definitions.","commonSituations":"Database migrations or ETL scripts copying definitions partially, admins editing custom properties directly, or restoring a definition backup where the version property was lost.","solutions":["Re-import the BPMN document to write SourceXmlCustomPropertyKey and SourceVersionCustomPropertyKey together","Restore the SourceVersionCustomPropertyKey to its correct value if you know the import version","Avoid editing BPMN-related custom properties directly; always go through the import API"],"exampleFix":"// before\n// manual migration wrote only the source property\n// after\nawait bpmnInterchangeService.ImportAsync(definitionId, originalBpmnDocument); // writes source + version together","handlingStrategy":"validation","validationCode":"var hasSource = definition.CustomProperties.TryGetValue<string>(SourceXmlCustomPropertyKey, out var s) && !string.IsNullOrEmpty(s);\nvar hasVersion = definition.CustomProperties.ContainsKey(SourceVersionCustomPropertyKey);\nif (hasSource && !hasVersion) throw new InvalidOperationException(\"BPMN source missing version; re-import\");","typeGuard":null,"tryCatchPattern":"try { var xml = service.Export(id); }\ncatch (BpmnExportUnavailableException ex) when (ex.Reason == BpmnExportUnavailableReason.SourceVersionUnknown) { await service.ImportAsync(id, originalDoc); xml = service.Export(id); }","preventionTips":["Never mutate BPMN custom properties directly; always import via the API","Keep source and version custom properties written together atomically","Audit migrations/scripts that touch definition custom properties"],"tags":["bpmn","export","custom-properties","version"],"backgroundTag":"bpmn-source-missing","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}