blender-syntax-data
This skill covers Blender data management: - Collections: hierarchy, visibility, object membership - Library linking/appending: bpy.data.libraries.load() - Library overrides: local editable copies of linked data (4.0+) - Asset system: marking, metadata, catalogs, AssetRepresentation - Data blocks:…
Works with
Agent Skills format with YAML frontmatter. Claude Code reads it as-is.
---
name: "blender-syntax-data"
description: "This skill covers Blender data management: - Collections: hierarchy, visibility, object membership - Library linking/appending: bpy.data.libraries.load() - Library overrides: local editable copies of linked data (4.0+) - Asset system: marking, metadata, catalogs, AssetRepresentation - Data blocks:…"
license: "MIT"
---
# blender-syntax-data
## Quick Reference
### Scope
This skill covers Blender data management:
- **Collections**: hierarchy, visibility, object membership
- **Library linking/appending**: `bpy.data.libraries.load()`
- **Library overrides**: local editable copies of linked data (4.0+)
- **Asset system**: marking, metadata, catalogs, `AssetRepresentation`
- **Data blocks**: creation, removal, fake users, orphan cleanup
- **Data transfer**: moving data between .blend files
### Critical Warnings
**NEVER** rename `scene.collection` — the root Scene Collection is read-only for name and cannot be deleted.
**NEVER** use proxies in Blender 4.0+ — proxies were completely removed. Use library overrides instead.
**NEVER** use `context.asset_file_handle` in Blender 5.0+ — it was removed. Use `context.asset` (`AssetRepresentation`) instead.
**NEVER** assume `bpy.data.libraries.load()` defaults to linking — the default is `link=False` (append). ALWAYS set `link=True` explicitly when linking.
**NEVER** automate asset catalogs via `bpy.ops.asset.catalog_new()` when you need specific names or UUIDs — the operator provides no parameter control. Edit `blender_assets.cats.txt` directly instead.
**ALWAYS** check that returned data blocks are not `None` after `libraries.load()` — missing names silently return `None`.
**ALWAYS** call `bpy.data.meshes.remove()` (or equivalent) to delete data blocks — Python `del` only removes the reference, not the data block.
---
## Decision Tree: Which Data Operation?
```
Need to organize objects in scene?
├── Group objects visually → Collection (§1)
├── Control per-view-layer visibility → LayerCollection (§1)
└── Control render/viewport visibility → Collection properties (§1)
Need data from another .blend file?
├── Read-only reference that stays synced → Link (§2, link=True)
├── Full local editable copy → Append (§2, link=False)
└── Editable copy that tracks source → Library Override (§3)
Need to manage assets?
├── Mark data block as asset → asset_mark() (§4)
├── Set metadata (tags, description) → AssetMetaData (§4)
├── Organize into catalogs → blender_assets.cats.txt (§4)
└── Access active asset in UI → context.asset (4.0+) (§4)
Need to manage data block lifecycle?
├── Create new data block → bpy.data.<collection>.new() (§5)
├── Prevent orphan cleanup → use_fake_user = True (§5)
├── Remove data block → bpy.data.<collection>.remove() (§5)
└── Clean all orphans → bpy.ops.outliner.orphans_purge() (§5)
```
---
## §1 Collections
Collections organize objects in a tree hierarchy. Every scene has exactly one root `scene.collection` (the "Scene Collection").
### Create and Nest Collections
```python
# Blender 3.x/4.x/5.x
import bpy
# Create a collection
building_col = bpy.data.collections.new("Building_A")
bpy.context.scene.collection.children.link(building_col)
# Nest collections
floors_col = bpy.data.collections.new("Floors")
building_col.children.link(floors_col)
```
### Object Membership
Objects can exist in multiple collections simultaneously. ALWAYS unlink from the source collection if you want exclusive membership.
```python
# Blender 3.x/4.x/5.x
obj = bpy.data.objects["Wall_01"]
walls_col.objects.link(obj) # Add to target
bpy.context.scene.collection.objects.unlink(obj) # Remove from source
```
### Collection Visibility
Two independent visibility systems exist:
| Property | Scope | Effect |
|----------|-------|--------|
| `collection.hide_viewport` | Global | Hidden in ALL viewports |
| `collection.hide_render` | Global | Hidden in ALL renders |
| `layer_collection.exclude` | Per view layer | Completely excluded from evaluation |
| `layer_collection.hide_viewport` | Per view layer | Hidden in viewport only |
```python
# Blender 3.x/4.x/5.x: per-view-layer visibility
view_layer = bpy.context.view_layer
layer_col = view_layer.layer_collection.children["Building_A"]
layer_col.exclude = True # Exclude from this view layer entirely
layer_col.hide_viewport = True # Hide in viewport, keep in evaluation
```
### Traverse Collection Tree
```python
# Blender 3.x/4.x/5.x
def walk_collection_tree(collection, depth=0):
print(" " * depth + collection.name)
for child in collection.children:
walk_collection_tree(child, depth + 1)
for obj in collection.objects:
print(" " * (depth + 1) + f"[{obj.type}] {obj.name}")
walk_collection_tree(bpy.context.scene.collection)
```
### Find LayerCollection by Name
`view_layer.layer_collection` mirrors the collection hierarchy but is NOT indexed by name at the top level. Recursive search is required.
```python
# Blender 3.x/4.x/5.x
def find_layer_collection(layer_col, name):
if layer_col.name == name:
return layer_col
for child in layer_col.children:
result = find_layer_collection(child, name)
if result:
return result
return None
lc = find_layer_collection(bpy.context.view_layer.layer_collection, "Building_A")
```
---
## §2 Library Linking and Appending
### bpy.data.libraries.load(): Recommended Method
```python
# Blender 3.x/4.x/5.x: Link (read-only reference)
filepath = "/path/to/library.blend"
with bpy.data.libraries.load(filepath, link=True) as (data_src, data_dst):
# data_src.<type> lists available names (strings)
# data_dst.<type> accepts list of names to load
data_dst.collections = ["BuildingModule_A"]
# ALWAYS check for None: missing names return None
for col in data_dst.collections:
if col is not None:
bpy.context.scene.collection.children.link(col)
```
```python
# Blender 3.x/4.x/5.x: Append (full local copy)
with bpy.data.libraries.load(filepath, link=False) as (data_src, data_dst):
data_dst.objects = ["Column_Type_A"]
data_dst.materials = ["Concrete_Exposed"]
for obj in data_dst.objects:
if obj is not None:
bpy.context.collection.objects.link(obj)
```
### Operator Method (Alternative)
```python
# Blender 3.x/4.x/5.x: link via operator
bpy.ops.wm.link(
filepath=filepath + "/Collection/BuildingModule_A",
directory=filepath + "/Collection/",
filename="BuildingModule_A"
)
# Append via operator
bpy.ops.wm.append(
filepath=filepath + "/Object/Column_Type_A",
directory=filepath + "/Object/",
filename="Column_Type_A"
)
```
### Key Parameters for libraries.load()
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `filepath` | str | required | Path to .blend file |
| `link` | bool | `False` | `True` = link (read-only), `False` = append (local copy) |
| `relative` | bool | `False` | Store path as relative |
| `assets_only` | bool | `False` | Only load data blocks marked as assets |
### Available Data Types in libraries.load()
Access via `data_src.<type>` and `data_dst.<type>`:
`actions`, `armatures`, `brushes`, `cameras`, `collections`, `curves`, `fonts`, `grease_pencils`, `images`, `lattices`, `lights`, `linestyles`, `masks`, `materials`, `meshes`, `metaballs`, `movieclips`, `node_groups`, `objects`, `paint_curves`, `palettes`, `particles`, `scenes`, `sounds`, `speakers`, `texts`, `textures`, `volumes`, `worlds`, `workspaces`
---
## §3 Library Overrides (4.0+)
Library overrides replace proxies (removed in 4.0). They create local editable copies of linked data that track the source — non-overridden properties sync automatically when the source updates.
### Create Override
```python
# Blender 4.0+: create library override from linked object
import bpy
# Step 1: Link data
with bpy.data.libraries.load("/path/to/library.blend", link=True) as (data_src, data_dst):
data_dst.collections = ["LinkedBuilding"]
for col in data_dst.collections:
if col is not None:
bpy.context.scene.collection.children.link(col)
# Step 2: Select linked object and create override
linked_obj = bpy.data.objects["LinkedBuilding"]
bpy.context.view_layer.objects.active = linked_obj
linked_obj.select_set(True)
# Creates local override: object is now editable
bpy.ops.object.make_override_library()
```
### Inspect Overrides
```python
# Blender 4.0+
obj = bpy.data.objects["LinkedBuilding"]
if obj.override_library:
ovr = obj.override_library
print(f"Source: {ovr.reference.name}")
print(f"Is system override: {ovr.is_system_override}")
for prop in ovr.properties:
print(f" Overridden path: {prop.rna_path}")
for op in prop.operations:
print(f" Operation: {op.operation}")
```
### Override Properties
| Property | Type | Description |
|----------|------|-------------|
| `obj.override_library` | `IDOverrideLibrary` or `None` | Override data (None if not an override) |
| `.reference` | `ID` | The linked data block being overridden |
| `.is_system_override` | `bool` | True if auto-generated (not user-created) |
| `.is_in_hierarchy` | `bool` | True if part of hierarchy override |
| `.hierarchy_root` | `ID` | Root of the override hierarchy |
| `.properties` | collection | Overridden properties list |
### Resync Overrides
```python
# Blender 4.0+: resync after source library changes
bpy.ops.object.override_library_resync()
```
---
## §4 Asset System
### Mark and Configure Assets
```python
# Blender 3.x/4.x/5.x: mark any ID data block as asset
import bpy
obj = bpy.data.objects["Column_Type_A"]
obj.asset_mark()
# Access metadata
meta = obj.asset_data # bpy.types.AssetMetaData
meta.description = "Reinforced concrete column, 300x300mm"
meta.author = "AEC Library"
meta.license = "CC-BY-4.0"
meta.copyright = "2024 AEC Corp"
# Tags
meta.tags.new("concrete", skip_if_exists=True)
meta.tags.new("structural", skip_if_exists=True)
# Catalog assignment (requires UUID)
meta.catalog_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
# Generate preview
bpy.ops.ed.lib_id_generate_preview(asset_id=obj.name)
# Clear asset status
obj.asset_clear()
```
### Asset Context Access (Version-Critical)
| Version | Access Pattern | Notes |
|---------|---------------|-------|
| Blender 3.x | `context.asset_file_handle` | `AssetHandle` — REMOVED in 5.0 |
| Blender 4.0+ | `context.asset` | `AssetRepresentation` — use this |
| Blender 5.0+ | `context.asset` | `AssetRepresentation` only — `asset_file_handle` gone |
```python
# Blender 4.0+: access active asset in asset browser
asset = bpy.context.asset # AssetRepresentation or None
if asset:
print(asset.name)
print(asset.full_library_path)
meta = asset.metadata # AssetMetaData
```
### Asset Catalogs
Catalog management has NO direct data API. Two approaches:
```python
# Approach 1: Operators (limited: no name/UUID control)
bpy.ops.asset.catalog_new()
bpy.ops.asset.catalog_delete()
bpy.ops.asset.catalogs_save()
```
```python
# Approach 2: Direct file edit (RECOMMENDED for automation)
# File: <asset_library_path>/blender_assets.cats.txt
# Format: UUID:path/to/catalog:Simple Name
# Example line: a1b2c3d4-e5f6-7890-abcd-ef1234567890:Structural/Columns:Columns
```
---
## §5 Data Block Lifecycle
### Create Data Blocks
```python
# Blender 3.x/4.x/5.x
mesh = bpy.data.meshes.new("MyMesh")
obj = bpy.data.objects.new("MyObject", mesh)
mat = bpy.data.materials.new("MyMaterial")
# REQUIRED: link object to a collection to make it visible
bpy.context.collection.objects.link(obj)
```
### Fake Users: Prevent Orphan Cleanup
Data blocks with zero users are removed on file save. Use `use_fake_user` to prevent this.
```python
# Blender 3.x/4.x/5.x
mat = bpy.data.materials.new("Template_Material")
mat.use_fake_user = True # Survives save even with zero real users
# Check user count
print(mat.users) # Number of real users
print(mat.use_fake_user) # True if fake user is set
```
### Remove Data Blocks
```python
# Blender 3.x/4.x/5.x
# Remove specific data block
bpy.data.meshes.remove(mesh)
bpy.data.objects.remove(obj)
bpy.data.materials.remove(mat)
# Purge ALL orphaned data blocks
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
```
### Batch Data Block Operations
```python
# Blender 3.x/4.x/5.x: iterate and filter data blocks
for mat in bpy.data.materials:
if mat.users == 0 and not mat.use_fake_user:
bpy.data.materials.remove(mat)
# SAFER: collect first, then remove (avoids iterator invalidation)
orphans = [m for m in bpy.data.materials if m.users == 0 and not m.use_fake_user]
for mat in orphans:
bpy.data.materials.remove(mat)
```
### Write Data to External File
```python
# Blender 3.x/4.x/5.x: save specific data blocks to a new .blend
data_blocks = {bpy.data.objects["Column_A"], bpy.data.materials["Concrete"]}
bpy.data.libraries.write("/path/to/output.blend", data_blocks)
# With options
bpy.data.libraries.write(
"/path/to/output.blend",
data_blocks,
path_remap="RELATIVE", # "NONE", "RELATIVE", "RELATIVE_ALL", "STRIP"
fake_user=False,
compress=True
)
```
---
## §6 Version Migration Summary
| Feature | Blender 3.x | Blender 4.0+ | Blender 5.0+ |
|---------|-------------|--------------|--------------|
| Object proxies | Available (deprecated) | REMOVED | REMOVED |
| Library overrides | Available | REQUIRED (replaces proxies) | REQUIRED |
| `context.asset_file_handle` | Available | Deprecated | REMOVED |
| `context.asset` | Not available | Available | REQUIRED |
| `AssetHandle` type | Available | Deprecated | REMOVED |
| `AssetRepresentation` | Not available | Available | REQUIRED |
| `context.active_file` (asset shelf) | N/A | Available | REMOVED |
---
## Reference Links
- [references/methods.md](references/methods.md) — Complete API signatures for collections, libraries, assets, overrides
- [references/examples.md](references/examples.md) — Working code examples for all data management operations
- [references/anti-patterns.md](references/anti-patterns.md) — What NOT to do with data management
### Official Sources
- https://docs.blender.org/api/current/bpy.types.Collection.html
- https://docs.blender.org/api/current/bpy.types.BlendDataLibraries.html
- https://docs.blender.org/api/current/bpy.types.AssetMetaData.html
- https://docs.blender.org/api/current/bpy.types.IDOverrideLibrary.html
- https://docs.blender.org/api/current/bpy.ops.asset.html
- https://developer.blender.org/docs/release_notes/4.0/python_api/
- https://developer.blender.org/docs/release_notes/5.0/python_api/More General & Other skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
grill-with-docs
mattpocock/skills
A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.

