Autodesk / Autodesk/revit-ifc

PR: IFC4 export of curtain walls crashes with AccessViolationException in FootPrintInfo

Open
#992 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
637
Forks
225
PR merge metrics
No merged PRs in 30d

Description

**Revit Version:** 2026.3.x (Design Automation RCE 26.3.0.37)
**IFC for Revit Addon Version:** 26.4.0
**Windows Version:** n/a — reproduced on Autodesk Design Automation for Revit (cloud), not a desktop Windows install

## Problem Description

Exporting a model containing curtain walls to **IFC4** terminates the Revit process with an access violation. On Design Automation the work item exits with `-1073741819` (`0xC0000005`) and produces no output; there is no catchable exception and no partial IFC.

```
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
at .CurveLoopCIter.getCurrent(CurveLoopCIter*, Owner*)
--------------------------------
at Autodesk.Revit.Proxy.DB.CurveLoopIteratorProxy.GetCurrent()
at Autodesk.Revit.DB.CurveLoopIterator.GetCurrent()
at Revit.IFC.Export.Utility.GeometryUtil.AllowComplexBoundary(XYZ, XYZ, CurveLoop, IList`1)
at Revit.IFC.Export.Utility.GeometryUtil.CreateIFCCurveFromCurveLoop(ExporterIFC, CurveLoop, Transform, XYZ)
at Revit.IFC.Export.Utility.FootPrintInfo.CreateFootprintShapeRepresentation(ExporterIFC)
at Revit.IFC.Export.Exporter.FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(...)
at Revit.IFC.Export.Exporter.CurtainSystemExporter.ExportCurtainObjectCommonAsContainer(...)
at Revit.IFC.Export.Exporter.CurtainSystemExporter.ExportBase(...)
at Revit.IFC.Export.Exporter.WallExporter.Export(ExporterIFC, Wall, GeometryElement ByRef, ProductWrapper)
at Revit.IFC.Export.Exporter.Exporter.ExportElementImpl(...)
at Revit.IFC.Export.Exporter.Exporter.ExportNonSpatialElements(...)
```

### Analysis

`FootPrintInfo` stores the `CurveLoop`s it is given **by reference**:

```csharp
public FootPrintInfo(IList curveLoops, Transform lcs = null)
{
ExtrusionBaseLoops = curveLoops; // no copy
...
}
```

Those loops are thin managed wrappers over native geometry owned by transient objects — `BodyExporter` passes loops obtained from `IFCExtrusionData.GetLoops()`, and the `ExtrusionAnalyzer` constructor passes loops derived from `extrusionAnalyzer.GetExtrusionBase()`.

The footprint representation is not built at that point. `FootPrintInfo` is held on the body/type info and `CreateFootprintShapeRepresentation` is only called later, from `FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem`, once the body export has completed. By then the geometry that owned those loops can already have been released, so iterating them reads freed memory.

Because `AccessViolationException` is a corrupted-state exception, this cannot be mitigated by callers on .NET 8 — the process is gone regardless of any `try`/`catch`.

### Why curtain walls specifically

`FamilyInstanceExporter` enables footprint collection for exactly two entity types:

```csharp
if (exportType.ExportInstance == IFCEntityType.IfcSlab || exportType.ExportInstance == IFCEntityType.IfcPlate)
bodyExporterOptions.CollectFootprintHandle = !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4;
```

Curtain panels export as `IfcPlate`, so any curtain-walled model is exposed on any schema IFC4 or newer. Exporting the same model to IFC2x3 succeeds, since `ExportAsOlderThanIFC4` then short-circuits the whole path — that is currently the only workaround available through export settings, and it costs the schema.

### Steps to reproduce

1. Take a model with curtain walls (ours is a ~286 MB facade model, Revit 2026).
2. Export to **IFC4 Design Transfer View**.
3. The process dies during `ExportNonSpatialElements`.

No sample RVT is available — the model is a confidential client project and I'm not able to share it, publicly or privately. The crash site is unambiguous from the stack trace, and the ownership problem is visible in the source, so I hope the analysis here is enough to act on without one.

### What we're running as a workaround

To be clear up front: **this is a workaround, not a fix.** I'm including it because it confirms the diagnosis and unblocks us, not as something to merge as-is. The underlying ownership problem needs solving properly, and that's a design call for maintainers rather than something I can make from outside.

We deep-copy the loops at capture time, while the source geometry is still alive:

```diff
public Transform ExtrusionBaseLCS { get; private set; }

+ static IList SafeCopy(IList curveLoops)
+ {
+ IList copies = new List();
+ if (curveLoops == null)
+ return copies;
+
+ foreach (CurveLoop curveLoop in curveLoops)
+ {
+ if (curveLoop == null)
+ continue;
+
+ try
+ {
+ CurveLoop copy = new CurveLoop();
+ foreach (Curve curve in curveLoop)
+ {
+ if (curve != null)
+ copy.Append(curve.Clone());
+ }
+ copies.Add(copy);
+ }
+ catch
+ {
+ // A loop we can't copy now is one we couldn't safely use later either.
+ }
+ }
+
+ return copies;
+ }
+
public FootPrintInfo(CurveLoop curveLoop, Transform lcs = null)
{
- ExtrusionBaseLoops.Add(curveLoop);
+ ExtrusionBaseLoops = SafeCopy(new List() { curveLoop });

public FootPrintInfo(IList curveLoops, Transform lcs = null)
{
- ExtrusionBaseLoops = curveLoops;
+ ExtrusionBaseLoops = SafeCopy(curveLoops);

// Only the first CurveLoop will be used as the foorprint
- ExtrusionBaseLoops = extrusionBoundaryLoops;
+ ExtrusionBaseLoops = SafeCopy(extrusionBoundaryLoops);
```

With this in place the same model exports successfully to IFC4 Design Transfer View, ~60 MB output, no crash. That it works is itself useful evidence: the loops really were outliving their owning geometry, rather than the model containing bad geometry.

Why I don't think it's the right fix:

- **It treats the symptom at the boundary.** The real issue is that `FootPrintInfo` is allowed to retain geometry it doesn't own across an arbitrary time gap. Copying defensively hides that rather than resolving it. Building the footprint representation eagerly, while the source geometry is definitely alive, or making the ownership explicit, both seem closer to the root.
- **`FootPrintInfo` may well not be the only place this happens.** Anywhere a `Curve` or `CurveLoop` obtained from transient geometry is cached and used later has the same latent hazard. I only went looking as far as the crash I hit.
- **It isn't free.** Cloning every loop for every slab and plate adds work to a hot path, on all models, to defend against a case that arises on some.
- **Swallowing the copy failure changes behaviour.** A loop that can't be copied now silently produces no footprint instead of crashing. Better than a hard crash, but it's a behavioural change that deserves a deliberate decision rather than a `catch` I picked.

Happy to open a PR if a defensive copy is the direction you'd want, but I'd rather not presume — the proper shape of the fix is yours to choose.

Contributor guide

Open the contributing guide

Research direction

Start by reading FootPrintInfo and its callers in BodyExporter and ExtrusionAnalyzer, then trace the delayed use through FamilyInstanceExporter and GeometryUtil.CreateIFCCurveFromCurveLoop. Reproduce an IFC4 curtain-wall export if a suitable model is available and compare it with IFC2x3. Done means the export no longer terminates with an access violation while retaining the intended footprint behavior, using an ownership strategy agreed with maintainers.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
computer-graphics
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.