The issue here is that with `ListNode.prev` and `LinkedList.last` being cursors, `LinkedList.first` is the only location keeping the first node alive. With the `list.first = node` assignment, there's no more reference that keeps the node previously pointed to by `first` alive and the cell is thus immediately destroyed -- both `next` and `list.prev` are now dangling pointers.
In order to verify this, you can add the following code after the type definitions: # note that this requires splitting the `object` definition into it's own # `ListNodeObj` type first proc ` =destroy`[T](x: var ListNodeObj[T]) = echo "destroying: ", x.val writeStackTrace() # log the current call hierarchy # the `next` field needs to be destroyed manually `=destroy`(x.next) Run This allows you to trace where and when a `ListNodeObj` cell gets destroyed. Note that passing a `ref` value to a parameter does _not_ keep the referenced location alive (that is, the reference counter is not incremented by one for the lifetime of the parameter). For fixing the issue, you need to swap the two `if`-statements. That way, a strong reference to the `prev` node exists (via `node.next`) when assigning to `list.first`, preventing it from being freed prematurely. You likely know this already, but the `.cursor` annotation only has an effect on locations with a type having an implicit or explicit destructor (for object fields, it also only applies when they're of `ref` or closure type). Since with `ref`s are not using destructors with `--mm:refc` (or anything else besides `--mm:arc|orc`), the `.cursor` annotation has no effect there.