# Insert on a nested (reference) block definition creates a ghost object

**URL:** <https://discourse.mcneel.com/t/insert-on-a-nested-reference-block-definition-creates-a-ghost-object/222874>\
**Category:** Scripting\
**Tags:** windows, bug\
**Created:** [September 21, 2026, 12:13pm UTC](https://discourse.mcneel.com/t/insert-on-a-nested-reference-block-definition-creates-a-ghost-object/222874 "2026-09-21T12:13:15Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![jirrivdb](https://sea3.discourse-cdn.com/mcneel/user_avatar/discourse.mcneel.com/jirrivdb/32/765129_2.png) [@jirrivdb](https://discourse.mcneel.com/u/jirrivdb)\
**Post date:** [September 21, 2026, 12:13pm UTC](https://discourse.mcneel.com/t/insert-on-a-nested-reference-block-definition-creates-a-ghost-object/222874/1 "2026-09-21T12:13:16Z")

</div>

Hi McNeel team. We have ran into a bug with linked blocks that sometimes causes kind of ghost objects to appear. I have used Claude and the Rhino MCP to get to the bottom of this and provide a reproducible example outside of our normal working files. So this is a bit of a co-authored bug report, but it is verified by me. -Jirri

## Summary

When a linked block is inserted, Rhino adds the block definitions nested inside the  
linked file to the document’s definition table as **reference** definitions  
(`InstanceDefinition.IsReference == True`, `IsTenuous == True`). These definitions are  
findable by name.

If you then run `-Insert` with the name of one of those nested definitions, Rhino  
creates a **top level, non-reference instance object** bound to that reference  
definition. The resulting object is broken in three ways:

1. **It cannot be deleted.** `_Delete`, `rs.DeleteObject()` and  
`RhinoDoc.Objects.Delete()` all refuse it and report no error. Only  
`RhinoDoc.Objects.Purge()` removes it.
2. **It is silently discarded on save.** Neither the object nor the definition is  
written to the 3dm. The part is simply gone when the file is reopened. No warning,  
no message, nothing in the command history.
3. **The document’s own bookkeeping is inconsistent about it.**  
`InstanceDefinition.InUse(0)` returns `False` while  
`InstanceDefinition.GetReferences(0)` returns that same object.
4. **It doesn’t have a gumball and cannot be moved**

We also have no way to recover this situation. A reference definition cannot be promoted in  
place: `InstanceDefinitionTable.ModifySourceArchive(...)` returns `True` but changes  
nothing, and `-BlockManager Properties <name> UpdateType Embedded|Linked` returns  
`False` and refuses.

## Environment

- Rhino 8 SR35, **8.35.26251.13001** , Windows 11 Pro 26200
- Reproduced from a pristine, 25 KB file authored through `Rhino.FileIO.File3dm`
- Reproduced using stock Rhino commands only. Our own plug-in was loaded in the test  
session; no part of the repro calls into it.

## Files

- `parent.3dm` — 25 KB. Contains one static block definition named `Widget`  
(a 100 mm cube) and one instance of it. Nothing else.

## Reproduction

Start Rhino 8 with a new, empty model, then:

**Step 1 — insert `parent.3dm` as a linked block**

```auto
_-Insert _File=_Yes _LinkMode=_Link _LayerStyle=_Active "<path>\parent.3dm" _Block 0 _Enter 0

```

The document now has two definitions:

| name | UpdateType | IsReference | IsTenuous |
| --- | --- | --- | --- |
| `parent` | Linked | False | False |
| `Widget` | Static | **True** | **True** |

`Widget` came along as a nested child of the linked file. It is a reference  
definition. It is returned by `InstanceDefinitionTable.Find("Widget")` and by `rs.IsBlock("Widget")`.

**Step 2 — insert an instance of the nested definition by name**

```auto
_-Insert "Widget" _Block 500,500,0 1 0 _Enter

```

A cube appears at 500,500,0. Rhino accepts this without any warning.

Observed state:

```auto
Widget.IsReference True
Widget.InUse(0) False <-- claims no top level references
Widget.GetReferences(0) 1 <-- but returns one
object.IsReference False <-- a normal, user owned object

```

**Step 3 — try to delete or move the new object**

Select the cube at 500,500,0 and press Delete, or run `_Delete`. Or try to use `Move`.

> **Expected:** the object is deleted.  
> **Actual:** nothing happens. The object stays. No error, no message.  
> `RhinoDoc.Objects.Delete()` returns `False`. Only `RhinoDoc.Objects.Purge()` works.

**Step 4 — save and reopen**

Save the model, then reopen it.

> **Expected:** the block instance is still there, or Rhino warns that it cannot be  
> saved.  
> **Actual:** the object and the `Widget` definition are both absent from the written  
> file. Silent data loss.

`verify_repro.py` performs steps 1–4 and prints a pass/fail line for each.

## Expected behaviour

Our prefence:

1. `-Insert` **promotes** the reference definition to a real definition when a top  
level instance of it is created, which is the state you get when the same block is  
inserted from file before the linked parent is loaded. That state is legal and  
works correctly today, so the target is well defined.

The `InUse(0)` / `GetReferences(0)` disagreement looks like a separate bookkeeping bug  
and may be the underlying cause: whatever `-Insert` uses to decide the insert is legal  
appears not to be what the object table actually records.

## How we hit this in production

We keep a library of several thousand parts as linked blocks, and we assemble models  
from linked module files. A module file routinely contains the same library parts a  
user will later place by hand.

So the order of operations decides whether a part is healthy or poisoned:

- Insert library part **first** , then link the module that also uses it → correct.  
The definition stays `IsReference=False` and is shared by both. We have 38 such  
definitions in the affected model, all fine.
- Link the module **first** , then insert the same library part → broken, as above.

Nothing in the UI distinguishes the two. In one production model we found 304  
definitions primed for this, and one part that had already been placed and was  
silently lost on every save. Tracking it down took a long time precisely because the  
evidence destroys itself: isolate the block, save, and it is gone.

## Scripts

Standalone, no dependencies on our codebase. Run with ScriptEditor  
(Python 3): `verify_repro.py`: runs the four steps above and asserts each symptom.

[verify\_repro.py](https://discourse.mcneel.com/uploads/short-url/7c2vuIDRyK1n3WJdycDvJ5prCh.py) (3.8 KB)

[parent.3dm](https://discourse.mcneel.com/uploads/short-url/9FIDqcvDT6JuDPJ0H8d3Bc9Pw0S.3dm) (24.7 KB)

---

<div class="post-metadata">

**Author:** ![dale](https://sea3.discourse-cdn.com/mcneel/user_avatar/discourse.mcneel.com/dale/32/685184_2.png) [@dale](https://discourse.mcneel.com/u/dale)\
**Post date:** [September 21, 2026, 5:19pm UTC](https://discourse.mcneel.com/t/insert-on-a-nested-reference-block-definition-creates-a-ghost-object/222874/2 "2026-09-21T17:19:11Z")

</div>

Hi @jirrivdb,

Thanks for the co-authored report — the scripted repro made this quick to check.

I ran your steps against Rhino 9 (9.0.26259.6303). The symptoms that caused actual harm are gone:

- The object deletes normally — `_Delete`, `rs.DeleteObject()` and `RhinoDoc.Objects.Delete()` all work. No `Purge()` needed.
- It moves normally and has a gumball.
- **It saves.** `Widget` is written to the 3dm as a real static definition with its geometry, and the instance points at it correctly. No silent data loss.

That last one is effectively the outcome you asked for: the definition gets materialized into the file on save.

Two things are still true in 9, both cosmetic as far as I can tell:

- `-Insert` still accepts a nested reference definition by name without warning, and the definition stays flagged `IsReference` / `IsTenuous` in the live document.
- `InUse(0)` still disagrees with `GetReferences(0)`. I went looking for a consequence and couldn’t find one — `Purge` does not delete the definition out from under a live instance.

Could you try your script on the Rhino 9 Beta and confirm? One heads-up: your script asserts the _buggy_ behaviour, so “all PASS” means the bug reproduced. On 9 you should see **FAIL on steps 3 and 4** — that’s the good result, just inverted from how it reads.

If you do still see the ghost objects on the Beta, let me know the build number and I’ll dig further.

Thanks,

– Dale

---

<div class="post-metadata">

**Author:** ![jirrivdb](https://sea3.discourse-cdn.com/mcneel/user_avatar/discourse.mcneel.com/jirrivdb/32/765129_2.png) [@jirrivdb](https://discourse.mcneel.com/u/jirrivdb)\
**Post date:** [September 21, 2026, 7:11pm UTC](https://discourse.mcneel.com/t/insert-on-a-nested-reference-block-definition-creates-a-ghost-object/222874/3 "2026-09-21T19:11:03Z")

</div>

Hi @dale

Thanks for the quick response. I just installed the Beta for Rhino 9 and ran the same tests. Everything works fine there indeed. I also ran into a [Youtrack](https://mcneel.myjetbrains.com/youtrack/issue/RH-90166) that was previously made for what I think is the same topic. This does pose a pretty big challenge for us currently.

Do you know if this is something that will remain planned for Rhino 9 or can it be fixed in a Rhino 8 service release?

---

<div class="post-metadata">

**Author:** ![dale](https://sea3.discourse-cdn.com/mcneel/user_avatar/discourse.mcneel.com/dale/32/685184_2.png) [@dale](https://discourse.mcneel.com/u/dale)\
**Post date:** [September 21, 2026, 7:22pm UTC](https://discourse.mcneel.com/t/insert-on-a-nested-reference-block-definition-creates-a-ghost-object/222874/4 "2026-09-21T19:22:15Z")

</div>

Hi @jirrivdb,

Thanks for testing the Rhino Beta,

We’ve made a number of Block-related fixes in the Rhino Beta. Most, if not all, of these issues pre-date Rhino 8. And together, these fixes are quite disruptive to core Rhino - so much that we won’t be back porting these to 8.x.

Thanks again,

– Dale

---

<div class="post-metadata">

**Author:** ![jirrivdb](https://sea3.discourse-cdn.com/mcneel/user_avatar/discourse.mcneel.com/jirrivdb/32/765129_2.png) [@jirrivdb](https://discourse.mcneel.com/u/jirrivdb)\
**Post date:** [September 28, 2026, 10:39am UTC](https://discourse.mcneel.com/t/insert-on-a-nested-reference-block-definition-creates-a-ghost-object/222874/5 "2026-09-28T10:39:32Z")

</div>

For anyone stumbling on this topic pre-Rhino 9, the best workaround we have found is to:  
-Select the ghost block, run Copy, run Paste  
-You now have a block that is top level and moveable  
-Then run Save and Revert which clears the ghost block and keeps the newly pasted functional block

We have built a warning into our block library interface that will explain this method to our colleagues, this is workable for now.
