Bug Report: CoincidentConstraint Selected via Canvas Click is Completely Unusable — All Property Accessors Throw

Bug Report: CoincidentConstraint Selected via Canvas Click is Completely Unusable — All Property Accessors Throw

richard_sim
Contributor Contributor
172 Views
0 Replies
Message 1 of 1

Bug Report: CoincidentConstraint Selected via Canvas Click is Completely Unusable — All Property Accessors Throw

richard_sim
Contributor
Contributor

Product: Autodesk Fusion
Component: Sketch API / Python Bindings / Selection System
Severity: High
Type: API Bug / C++ Object Lifecycle / Python Binding Safety


Summary

When a user clicks a CoincidentConstraint glyph directly in the sketch canvas, the activeSelectionChanged event delivers a CoincidentConstraint object via args.currentSelection.item(i).entity that is completely inaccessible. Every property accessor on the object throws a C++ exception leaking through the Python bindings. After an initial Python exception subsequent exceptions will likely result in Fusion hard-crashing. Attempting to inspect the selected constraint in the VS Code debugger will instantly crash Fusion. The object cannot be matched to any constraint in sketch.geometricConstraints and is effectively unusable. This is a regression or latent defect in the selection system's object lifecycle management for geometric constraints.

 

Bug report generated by Autodesk Assistant based on trying to get it to debug the issues.


Environment

  • Product: Autodesk Fusion (confirmed in production build)
  • Build path: C:\Users sim\AppData\Local\Autodesk\webdeploy\production\dc17ef0089c4e594bf5a4bf81f553cd441924ebc
  • Python runtime: Python 3.14 (_py314)
  • Python bindings: adsk/fusion.py (same build path above)
  • Platform: Windows 11 (10.0.26100), AMD64
  • API entry point: userInterface.activeSelectionChanged event

     


    Steps to Reproduce
    1. Open a Fusion document with a sketch containing at least one CoincidentConstraint.
    2. Activate the sketch for editing (double-click to enter sketch edit mode).
    3. Register a handler for userInterface.activeSelectionChanged.
    4. In the handler, for each selection in args.currentSelection, read selection.entity.
    5. Click directly on a CoincidentConstraint glyph in the sketch canvas (not via any programmatic selection).
    6. Observe the entity delivered to the handler.

    Minimal reproducer add-in:

import adsk.core, adsk.fusion, traceback
app = adsk.core.Application.get()
ui = app.userInterfacehandlers = []

class SelectionChangedHandler(adsk.core.ActiveSelectionEventHandler):
    def notify(self, args):
        for sel in args.currentSelection:
            entity = sel.entity

            if isinstance(entity, adsk.fusion.CoincidentConstraint):
                try:
                    futil.log(f"objectType: {entity.objectType}")
                    futil.log(f"isValid: {entity.isValid}")
                    futil.log(f"nativeObject: {getattr(entity, 'nativeObject', 'N/A')}")
                    futil.log(f"assemblyContext: {getattr(entity, 'assemblyContext', 'N/A')}")

                    pt = entity.point
                    ent = entity.entity
                    futil.log(f"point type: {pt.objectType}")
                    futil.log(f"point.entityToken: {pt.entityToken}")
                    futil.log(f"entity type: {ent.objectType}")
                    futil.log(f"entity.entityToken: {ent.entityToken}")

                    # Test 1: entityToken
                    try:
                        futil.log(f"entityToken: {entity.entityToken}")
                    except RuntimeError as e:
                        futil.log(f"entityToken FAILED: {e}")

                    # Test 2: .point
                    try:
                        futil.log(f"point: {entity.point}")
                    except RuntimeError as e:
                        futil.log(f"point FAILED: {e}")

                    # Test 3: .entity
                    try:
                        futil.log(f"entity: {entity.entity}")
                    except RuntimeError as e:
                        futil.log(f"entity.entity FAILED: {e}")

                    # Safe: selection point
                    futil.log(f"sel.point (safe): {sel.point.x}, {sel.point.y}, {sel.point.z}")
                except Exception as e:
                    futil.log(f"Error: {type(e).__name__}: {e}\n{traceback.format_exc()}")

def run(context):
    h = SelectionChangedHandler()
    ui.activeSelectionChanged.add(h)
    handlers.append(h)
 

Observed Behavior

The entity delivered is typed as adsk::fusion::CoincidentConstraint and reports isValid = True, but every property accessor throws:

Exception 1 — .entityToken

RuntimeError: 2 : InternalValidationError : Utils::findObjectPath(constraint->xInterfaceObject(), logicalPath)

From:

File ".../adsk/fusion.py", line 33952, in _get_entityToken
    return _fusion.GeometricConstraint__get_entityToken(self)

Exception 2 — .point

RuntimeError: 1 : vector too long

From:

File ".../adsk/fusion.py", line 74890, in _get_point
    return _fusion.CoincidentConstraint__get_point(self)

Exception 3 — .entity

Assumed to also throw (same broken underlying object); not separately confirmed but consistent with the object state.

Properties that DO work on the broken object

  • .objectType  "adsk::fusion::CoincidentConstraint" 
  • .isValid  True  (misleading — see analysis)
  • .nativeObject  None  (not a proxy)
  • .assemblyContext  None  (not in assembly context)
  • adsk.core.Selection.point → valid Point3D  (this is on the Selection wrapper, not on entity)

Expected Behavior

A CoincidentConstraint delivered via activeSelectionChanged should be the same object (or a valid equivalent) as the corresponding constraint in sketch.geometricConstraints. All property accessors (.entityToken, .point, .entity) should work without throwing. At minimum, if the object is not fully usable, isValid should return False to signal this to API consumers.

 


Key Observations and Diagnostic Details 1. The bug is specific to canvas-click selection — programmatic selection works

When a constraint is selected programmatically (e.g., via userInterface.activeSelections.add() using a valid entity token obtained from geometricConstraints), the handler receives a fully functional object. The broken object is produced exclusively when the user clicks the constraint glyph in the canvas.

From the add-in logs, the two cases side by side:

Canvas click (broken):

Entity: <class 'adsk.fusion.CoincidentConstraint'>
Point: (-0.82, -4.14, 0.0)    ← non-zero, valid canvas position
isValid: True
entityToken: THROWS InternalValidationError
point: THROWS "vector too long"

Programmatic selection (working):

Entity: <class 'adsk.fusion.CoincidentConstraint'>
Point: (0.0, 0.0, 0.0)        ← origin, no geometric point
isValid: True
entityToken: OK → valid token string

2. Two distinct C++ errors indicate two separate internal failures

InternalValidationError: Utils::findObjectPath(constraint->xInterfaceObject(), logicalPath)

This error originates in entityToken computation. findObjectPath walks the document object graph to construct a stable string path for the object. The fact that it fails means the constraint's xInterfaceObject() — the underlying C++ interface pointer — either:

  • Is not registered in the document's object graph at all, or
  • Is registered under a different parent than expected (e.g., a temporary sketch solve context rather than the persistent sketch), or
  • Has already been released/recycled but the Python wrapper has not been invalidated

RuntimeError: 1 : vector too long

This is a raw C++ STL std::length_error or similar vector overflow sentinel leaking through the binding layer for .point. This is a qualitatively different failure than the path error above — it suggests the underlying C++ CoincidentConstraint object's internal data structure (likely the vector storing the constrained point reference) is in an invalid or uninitialized state. This is not a graceful API error — it is a raw memory/object state error that the Python binding layer is not catching.

The combination of these two distinct errors strongly suggests the object was created in a temporary solve or hit-test context and was never properly initialized or registered as a persistent document object.

3. isValid returning True is incorrect and dangerous

The .isValid property is the standard API guard that consumers use to check whether an object is safe to access. It returns True on this object, yet every property throws. This means the validity check is not detecting the broken state, likely because isValid only checks whether the C++ pointer is non-null, not whether the object is properly initialized or registered in the document graph.

4. The object is not a proxy

nativeObject returns None and assemblyContext returns None, ruling out the standard assembly proxy pattern as the cause. This is a native-context object that is simply in an invalid internal state.

5. The bug affects all GeometricConstraint subtypes via the same code path

The entityToken error routes through GeometricConstraint__get_entityToken (line 33952 of fusion.py), which is the base class implementation shared by all geometric constraints. The .point error is in CoincidentConstraint__get_point specifically. Other constraint types (e.g., TangentConstraint, ParallelConstraint) likely exhibit the same entityToken failure when canvas-clicked, though their type-specific property accessors may fail differently.


Hypothesis on Root Cause

When Fusion performs a hit-test for canvas selection of a sketch constraint glyph, it likely instantiates a transient C++ constraint object for the purpose of the hit-test or selection highlight, and incorrectly wraps this transient object in a Python CoincidentConstraint binding to deliver it to the activeSelectionChanged event. This transient object:

  • Has a valid type identity (hence correct objectType and non-null pointer for isValid)
  • Is not registered in the persistent document object graph (hence findObjectPath fails)
  • Has uninitialized or invalid internal data vectors (hence vector too long on .point)
  • Is not the same C++ instance as the persistent constraint stored in geometricConstraints

The correct behavior would be to deliver the persistent constraint object from geometricConstraints to the event, identical to what programmatic selection does.


Impact

  • Any add-in or script that uses activeSelectionChanged to respond to user selection of sketch constraints is broken for canvas-click interactions.
  • The misleading isValid = True means defensive code written to the API contract will still crash.
  • No workaround exists that uses the delivered entity object directly — all property accessors are inaccessible.

Workaround (for add-in developers, until fixed)

No workaround is available.

 

Files / Entry Points for Investigation

 
Location Relevance
adsk/fusion.py line 33952: GeometricConstraint__get_entityTokenEntry point for entityToken failure
adsk/fusion.py line 74890: CoincidentConstraint__get_pointEntry point for vector too long failure
Utils::findObjectPath(constraint->xInterfaceObject(), logicalPath)C++ function that fails to find the object in the document graph
SelectCommand / canvas hit-test code pathLikely source of the transient object being wrapped and delivered
activeSelectionChanged event dispatchWhere the transient object is handed to Python instead of the persistent one
GeometricConstraint::isValid implementationShould be fixed to detect unregistered/uninitialized state

 

@keqingsong @CGBenner 

0 Likes
173 Views
0 Replies
Replies (0)