Blender Agent: Version Migrator
Systematic migration process for Blender Python code across major versions. This agent skill activates when migrating, porting, updating, or upgrading Blender Python scripts or addons between versions 3.x, 4.x, and 5.x.
Dependencies:
1. Quick Reference: Migration Paths
Supported Migration Paths
| From |
To |
Complexity |
Key Changes |
| 3.x |
4.0 |
HIGH |
Context overrides, mesh attributes, node interface, bone collections |
| 4.0 |
4.1 |
MEDIUM |
Auto-smooth removal, light probe renames, displacement method move |
| 4.1 |
4.2 |
MEDIUM |
Extension manifest, EEVEE rename, static IDProperties |
| 4.2 |
4.3 |
HIGH |
Grease Pencil rewrite, AttributeGroup split, EEVEE property removal |
| 4.3 |
4.4 |
MEDIUM |
super().init required, Sequence→Strip, Slotted Actions |
| 4.4 |
4.5 |
LOW |
Minor deprecations only |
| 4.x |
5.0 |
HIGH |
BGL removal, compositor change, slotted actions mandatory |
| 5.0 |
5.1 |
LOW |
Python 3.13, VSE property renames (deprecated) |
| 3.x |
5.x |
CRITICAL |
ALL of the above — execute sequentially |
Migration Execution Order
For a 3.x → 5.x migration, ALWAYS apply changes in this exact order:
- 3.x → 4.0 changes
- 4.0 → 4.1 changes
- 4.1 → 4.2 changes
- 4.2 → 4.3 changes
- 4.3 → 4.4 changes
- 4.4 → 5.0 changes
- 5.0 → 5.1 changes
NEVER skip intermediate versions. Each version removes APIs deprecated in prior versions.
2. Migration Agent Process
When migrating Blender Python code, follow these steps in order:
Step 1: Identify Source and Target Versions
# Determine current version compatibility from code signals
# Check for these indicators:
# - bl_info dict → 3.x / 4.0 / 4.1 addon
# - blender_manifest.toml → 4.2+ extension
# - import bgl → pre-5.0 code
# - context override dicts on bpy.ops → pre-4.0 code
# - mesh.use_auto_smooth → pre-4.1 code
# - NodeTree.inputs.new() → pre-4.0 code
Step 2: Scan for Affected APIs
Search the codebase for ALL patterns listed in the Find-and-Replace Tables (Section 3). Mark each occurrence with the version transition that affects it.
Step 3: Apply Changes Per Version Transition
Apply changes from the relevant migration checklists below. Process ONE version transition at a time.
Step 4: Update Metadata
- Replace
bl_info with blender_manifest.toml if targeting 4.2+
- Update minimum version requirements
- Update any version checks in the code
Step 5: Validate
- Search for ANY remaining references to removed APIs
- Verify all
import statements reference available modules
- Check that
gpu.state is restored at end of draw callbacks
- Confirm
super().__init__(*args, **kwargs) in all Blender type subclasses (4.4+)
3. Find-and-Replace Tables
3.x → 4.0 Replacements
| Find |
Replace With |
Category |
bpy.ops.*.call(override_dict, (dict as first arg) |
with bpy.context.temp_override(**kwargs): |
Context |
mesh.edges[i].bevel_weight |
mesh.attributes.get("bevel_weight_edge").data[i].value |
Mesh |
mesh.edges[i].crease |
mesh.attributes.get("crease_edge").data[i].value |
Mesh |
obj.face_maps |
Integer face attributes |
Mesh |
mesh.calc_normals() |
Remove call (auto-calculated) |
Mesh |
bone.layers[i] |
bone.collections |
Armature |
pose.bone_groups |
Bone collections with colors |
Armature |
NodeTree.inputs.new(type, name) |
NodeTree.interface.new_socket(name=name, in_out='INPUT', socket_type=type) |
Nodes |
NodeTree.outputs.new(type, name) |
NodeTree.interface.new_socket(name=name, in_out='OUTPUT', socket_type=type) |
Nodes |
node.inputs["Subsurface"] |
node.inputs["Subsurface Weight"] |
Shader |
node.inputs["Specular"] |
node.inputs["Specular IOR Level"] |
Shader |
node.inputs["Transmission"] |
node.inputs["Transmission Weight"] |
Shader |
gpu.shader.from_builtin('3D_UNIFORM_COLOR') |
gpu.shader.from_builtin('POLYLINE_UNIFORM_COLOR') |
GPU |
gpu.shader.from_builtin('3D_FLAT_COLOR') |
gpu.shader.from_builtin('POLYLINE_FLAT_COLOR') |
GPU |
bpy.ops.import_scene.obj( |
bpy.ops.wm.obj_import( |
IO |
bpy.ops.export_scene.obj( |
bpy.ops.wm.obj_export( |
IO |
filename= (in IO operators) |
filepath= |
IO |
4.0 → 4.1 Replacements
| Find |
Replace With |
Category |
mesh.use_auto_smooth = True |
Remove (always active) |
Mesh |
mesh.auto_smooth_angle |
Use "Smooth by Angle" modifier |
Mesh |
mesh.calc_normals_split() |
Remove (use mesh.corner_normals) |
Mesh |
mesh.create_normals_split() |
Remove |
Mesh |
mesh.free_normals_split() |
Remove |
Mesh |
probe.type == 'CUBEMAP' |
probe.type == 'SPHERE' |
Light Probe |
probe.type == 'PLANAR' |
probe.type == 'PLANE' |
Light Probe |
probe.type == 'GRID' |
probe.type == 'VOLUME' |
Light Probe |
mat.cycles.displacement_method |
mat.displacement_method |
Material |
SUBSAMPLING_3x3 |
BOX |
Sequencer |
4.1 → 4.2 Replacements
| Find |
Replace With |
Category |
bl_info = { (if targeting 4.2+) |
blender_manifest.toml file |
Extension |
BLENDER_EEVEE (render engine) |
BLENDER_EEVEE_NEXT |
Render |
scene.cycles.motion_blur_position |
scene.render.motion_blur_position |
Render |
scene.eevee.use_motion_blur |
scene.render.use_motion_blur |
Render |
object.load_reference_image |
object.empty_image_add |
Object |
4.2 → 4.3 Replacements
| Find |
Replace With |
Category |
bpy.types.AttributeGroup |
AttributeGroupMesh / AttributeGroupPointCloud / etc. |
Attributes |
scene.eevee.use_ssr |
Remove (engine-internal) |
EEVEE |
scene.eevee.use_bloom |
Remove (engine-internal) |
EEVEE |
scene.eevee.* (40+ legacy props) |
Remove or check 4.3 release notes |
EEVEE |
| ALL Grease Pencil API calls |
Complete rewrite required |
Grease Pencil |
4.3 → 4.4 Replacements
| Find |
Replace With |
Category |
class MyOp(bpy.types.Operator): (no super init) |
Add super().__init__(*args, **kwargs) |
Classes |
bpy.types.Sequence |
bpy.types.Strip |
VSE |
action.fcurves.new( (for new code) |
Slotted Actions API |
Animation |
paint.brush (write access) |
Read-only; use brush asset system |
Paint |
4.x → 5.0 Replacements
| Find |
Replace With |
Category |
import bgl |
import gpu |
Drawing |
bgl.glEnable(bgl.GL_BLEND) |
gpu.state.blend_set('ALPHA') |
Drawing |
bgl.glDisable(bgl.GL_BLEND) |
gpu.state.blend_set('NONE') |
Drawing |
bgl.glLineWidth(w) |
gpu.state.line_width_set(w) |
Drawing |
bgl.glEnable(bgl.GL_DEPTH_TEST) |
gpu.state.depth_test_set('LESS_EQUAL') |
Drawing |
bgl.glDisable(bgl.GL_DEPTH_TEST) |
gpu.state.depth_test_set('NONE') |
Drawing |
bgl.glPointSize(s) |
gpu.state.point_size_set(s) |
Drawing |
bgl.glDepthMask(GL_TRUE) |
gpu.state.depth_mask_set(True) |
Drawing |
image.gl_load() / image.bindcode |
gpu.texture.from_image(image) |
Drawing |
gpu.types.GPUShader() |
gpu.shader.create_from_info() |
Drawing |
scene['cycles'] |
scene.cycles.property_name (attribute access) |
Properties |
scene.node_tree (compositor) |
scene.compositing_node_group |
Compositor |
scene.use_nodes (compositor) |
Remove (always active) |
Compositor |
brush.sculpt_tool |
brush.sculpt_brush_type |
Sculpt |
strip.end_frame (VSE) |
strip.length |
VSE |
action.fcurves (legacy) |
Slotted Actions channelbag API |
Animation |
BLENDER_EEVEE_NEXT |
BLENDER_EEVEE (reverted) |
Render |
5.0 → 5.1 Replacements
| Find |
Replace With |
Category |
sculpt.sample_color |
paint.sample_color |
Sculpt |
frame_final_duration |
duration (deprecated, removed in 6.0) |
VSE |
frame_final_start |
left_handle (deprecated, removed in 6.0) |
VSE |
frame_final_end |
right_handle (deprecated, removed in 6.0) |
VSE |
4. Decision Tree: Compatibility Shims vs Clean Migration
MIGRATION STRATEGY DECISION
│
├── Target SINGLE Blender version?
│ └── YES → Clean migration: remove ALL legacy code, use target API only
│
├── Target VERSION RANGE (e.g., 3.x + 4.x)?
│ ├── Range spans 3.x and 4.0+ ?
│ │ └── YES → Use version-safe wrappers with bpy.app.version checks
│ ├── Range spans 4.x and 5.0+ ?
│ │ └── YES → Use version-safe wrappers; NEVER import bgl at module level
│ └── Range within single major (e.g., 4.0–4.4)?
│ └── Use hasattr() feature detection for minor version differences
│
└── Target LATEST only (drop legacy)?
└── Clean migration: target version API only, no compatibility code
When to Use Compatibility Shims
ALWAYS use compatibility shims when:
- The addon targets 2+ major Blender versions simultaneously
- The addon is distributed to users on different Blender versions
NEVER use compatibility shims when:
- The migration targets a single Blender version
- The code is an internal tool pinned to one Blender version
Compatibility Module Pattern
# compat.py: Version-safe compatibility layer
import bpy
BLENDER_4 = bpy.app.version >= (4, 0, 0)
BLENDER_41 = bpy.app.version >= (4, 1, 0)
BLENDER_42 = bpy.app.version >= (4, 2, 0)
BLENDER_43 = bpy.app.version >= (4, 3, 0)
BLENDER_44 = bpy.app.version >= (4, 4, 0)
BLENDER_5 = bpy.app.version >= (5, 0, 0)
BLENDER_51 = bpy.app.version >= (5, 1, 0)
def apply_modifier(obj, modifier_name):
if BLENDER_4:
with bpy.context.temp_override(object=obj, active_object=obj):
bpy.ops.object.modifier_apply(modifier=modifier_name)
else:
override = {"object": obj, "active_object": obj}
bpy.ops.object.modifier_apply(override, modifier=modifier_name)
Conditional Import Pattern for BGL → gpu
# NEVER import bgl at module level in multi-version addons
import gpu
# For addons targeting 3.x–4.x (before 5.0 removal):
try:
import bgl
HAS_BGL = True
except ImportError:
HAS_BGL = False # Blender 5.0+
# ALWAYS use gpu module for new code regardless of version
5. EEVEE Identifier Migration
The EEVEE render engine identifier changed across versions. ALWAYS use this lookup:
| Blender Version |
EEVEE Identifier |
| 3.x |
BLENDER_EEVEE |
| 4.0 – 4.1 |
BLENDER_EEVEE |
| 4.2 – 4.x |
BLENDER_EEVEE_NEXT |
| 5.0+ |
BLENDER_EEVEE |
def get_eevee_identifier():
if bpy.app.version >= (5, 0, 0):
return 'BLENDER_EEVEE'
elif bpy.app.version >= (4, 2, 0):
return 'BLENDER_EEVEE_NEXT'
else:
return 'BLENDER_EEVEE'
6. Extension System Migration (4.2+)
When migrating from legacy addon to extension system:
- Create
blender_manifest.toml in addon root
- Remove
bl_info dict from __init__.py
- Set
blender_version_min = "4.2.0" (or target minimum)
- Add
id matching the package directory name
- Add
tagline (max 64 chars, no trailing punctuation)
- Add
type = "add-on" or type = "theme"
- Add network permissions if addon accesses internet:
[permissions] → network = "Reason for access"
- Add
license = ["SPDX:GPL-3.0-or-later"] (or appropriate SPDX)
7. Critical Migration Rules
- NEVER use
import bgl in code targeting Blender 5.0+ — the module is REMOVED.
- ALWAYS restore
gpu.state to defaults at the end of every draw callback.
- NEVER pass a dict as the first argument to
bpy.ops in Blender 4.0+ — use context.temp_override().
- ALWAYS add
super().__init__(*args, **kwargs) to Blender type subclasses in 4.4+.
- NEVER suppress
DeprecationWarning — it hides migration signals.
- NEVER use
try/except AttributeError as a version check — use bpy.app.version or hasattr().
- ALWAYS set
POLYLINE_UNIFORM_COLOR shader uniforms: viewportSize and lineWidth.
- ALWAYS migrate Grease Pencil code completely when targeting 4.3+ — partial migration breaks.
- NEVER assign embedded IDs to
PointerProperty in 4.3+.
- ALWAYS check the deprecation timeline in blender-errors-version before using any API.
8. Reference Links
- references/methods.md — Complete API migration mappings organized by version transition
- references/examples.md — Before/after code examples for each migration path
- references/anti-patterns.md — Common migration mistakes with explanations
Official Documentation
1---2name: blender-agents-version-migrator3description: Use when migrating, porting, or upgrading Blender Python scripts and addons across major versions (3.x to 4.x to 5.x). Provides a systematic migration process covering API renames, removed functions, changed parameters, extension system migration, BGL to GPU module conversion, and bone collection migration. Prevents incomplete migrations that compile but fail at runtime. Keywords: migration, porting, upgrade, version, 3.x to 4.x, 4.x to 5.x, API rename, bgl to gpu, extension migration, bl_info to manifest, bone collection, script stopped working after update, upgrade addon to new Blender.4license: MIT5---67# Blender Agent: Version Migrator89Systematic migration process for Blender Python code across major versions. This agent skill activates when migrating, porting, updating, or upgrading Blender Python scripts or addons between versions 3.x, 4.x, and 5.x.1011**Dependencies**:12- [blender-core-versions](../../core/blender-core-versions/SKILL.md) — version matrix, detection API, breaking changes13- [blender-errors-version](../../errors/blender-errors-version/SKILL.md) — error diagnosis for version-related failures1415---1617## 1. Quick Reference: Migration Paths1819### Supported Migration Paths2021| From | To | Complexity | Key Changes |22|------|----|-----------|-------------|23| 3.x | 4.0 | HIGH | Context overrides, mesh attributes, node interface, bone collections |24| 4.0 | 4.1 | MEDIUM | Auto-smooth removal, light probe renames, displacement method move |25| 4.1 | 4.2 | MEDIUM | Extension manifest, EEVEE rename, static IDProperties |26| 4.2 | 4.3 | HIGH | Grease Pencil rewrite, AttributeGroup split, EEVEE property removal |27| 4.3 | 4.4 | MEDIUM | super().__init__ required, Sequence→Strip, Slotted Actions |28| 4.4 | 4.5 | LOW | Minor deprecations only |29| 4.x | 5.0 | HIGH | BGL removal, compositor change, slotted actions mandatory |30| 5.0 | 5.1 | LOW | Python 3.13, VSE property renames (deprecated) |31| 3.x | 5.x | CRITICAL | ALL of the above — execute sequentially |3233### Migration Execution Order3435For a 3.x → 5.x migration, ALWAYS apply changes in this exact order:361. 3.x → 4.0 changes372. 4.0 → 4.1 changes383. 4.1 → 4.2 changes394. 4.2 → 4.3 changes405. 4.3 → 4.4 changes416. 4.4 → 5.0 changes427. 5.0 → 5.1 changes4344NEVER skip intermediate versions. Each version removes APIs deprecated in prior versions.4546---4748## 2. Migration Agent Process4950When migrating Blender Python code, follow these steps in order:5152### Step 1: Identify Source and Target Versions5354```python55# Determine current version compatibility from code signals56# Check for these indicators:57# - bl_info dict → 3.x / 4.0 / 4.1 addon58# - blender_manifest.toml → 4.2+ extension59# - import bgl → pre-5.0 code60# - context override dicts on bpy.ops → pre-4.0 code61# - mesh.use_auto_smooth → pre-4.1 code62# - NodeTree.inputs.new() → pre-4.0 code63```6465### Step 2: Scan for Affected APIs6667Search the codebase for ALL patterns listed in the Find-and-Replace Tables (Section 3). Mark each occurrence with the version transition that affects it.6869### Step 3: Apply Changes Per Version Transition7071Apply changes from the relevant migration checklists below. Process ONE version transition at a time.7273### Step 4: Update Metadata7475- Replace `bl_info` with `blender_manifest.toml` if targeting 4.2+76- Update minimum version requirements77- Update any version checks in the code7879### Step 5: Validate8081- Search for ANY remaining references to removed APIs82- Verify all `import` statements reference available modules83- Check that `gpu.state` is restored at end of draw callbacks84- Confirm `super().__init__(*args, **kwargs)` in all Blender type subclasses (4.4+)8586---8788## 3. Find-and-Replace Tables8990### 3.x → 4.0 Replacements9192| Find | Replace With | Category |93|------|-------------|----------|94| `bpy.ops.*.call(override_dict,` (dict as first arg) | `with bpy.context.temp_override(**kwargs):` | Context |95| `mesh.edges[i].bevel_weight` | `mesh.attributes.get("bevel_weight_edge").data[i].value` | Mesh |96| `mesh.edges[i].crease` | `mesh.attributes.get("crease_edge").data[i].value` | Mesh |97| `obj.face_maps` | Integer face attributes | Mesh |98| `mesh.calc_normals()` | Remove call (auto-calculated) | Mesh |99| `bone.layers[i]` | `bone.collections` | Armature |100| `pose.bone_groups` | Bone collections with colors | Armature |101| `NodeTree.inputs.new(type, name)` | `NodeTree.interface.new_socket(name=name, in_out='INPUT', socket_type=type)` | Nodes |102| `NodeTree.outputs.new(type, name)` | `NodeTree.interface.new_socket(name=name, in_out='OUTPUT', socket_type=type)` | Nodes |103| `node.inputs["Subsurface"]` | `node.inputs["Subsurface Weight"]` | Shader |104| `node.inputs["Specular"]` | `node.inputs["Specular IOR Level"]` | Shader |105| `node.inputs["Transmission"]` | `node.inputs["Transmission Weight"]` | Shader |106| `gpu.shader.from_builtin('3D_UNIFORM_COLOR')` | `gpu.shader.from_builtin('POLYLINE_UNIFORM_COLOR')` | GPU |107| `gpu.shader.from_builtin('3D_FLAT_COLOR')` | `gpu.shader.from_builtin('POLYLINE_FLAT_COLOR')` | GPU |108| `bpy.ops.import_scene.obj(` | `bpy.ops.wm.obj_import(` | IO |109| `bpy.ops.export_scene.obj(` | `bpy.ops.wm.obj_export(` | IO |110| `filename=` (in IO operators) | `filepath=` | IO |111112### 4.0 → 4.1 Replacements113114| Find | Replace With | Category |115|------|-------------|----------|116| `mesh.use_auto_smooth = True` | Remove (always active) | Mesh |117| `mesh.auto_smooth_angle` | Use "Smooth by Angle" modifier | Mesh |118| `mesh.calc_normals_split()` | Remove (use `mesh.corner_normals`) | Mesh |119| `mesh.create_normals_split()` | Remove | Mesh |120| `mesh.free_normals_split()` | Remove | Mesh |121| `probe.type == 'CUBEMAP'` | `probe.type == 'SPHERE'` | Light Probe |122| `probe.type == 'PLANAR'` | `probe.type == 'PLANE'` | Light Probe |123| `probe.type == 'GRID'` | `probe.type == 'VOLUME'` | Light Probe |124| `mat.cycles.displacement_method` | `mat.displacement_method` | Material |125| `SUBSAMPLING_3x3` | `BOX` | Sequencer |126127### 4.1 → 4.2 Replacements128129| Find | Replace With | Category |130|------|-------------|----------|131| `bl_info = {` (if targeting 4.2+) | `blender_manifest.toml` file | Extension |132| `BLENDER_EEVEE` (render engine) | `BLENDER_EEVEE_NEXT` | Render |133| `scene.cycles.motion_blur_position` | `scene.render.motion_blur_position` | Render |134| `scene.eevee.use_motion_blur` | `scene.render.use_motion_blur` | Render |135| `object.load_reference_image` | `object.empty_image_add` | Object |136137### 4.2 → 4.3 Replacements138139| Find | Replace With | Category |140|------|-------------|----------|141| `bpy.types.AttributeGroup` | `AttributeGroupMesh` / `AttributeGroupPointCloud` / etc. | Attributes |142| `scene.eevee.use_ssr` | Remove (engine-internal) | EEVEE |143| `scene.eevee.use_bloom` | Remove (engine-internal) | EEVEE |144| `scene.eevee.*` (40+ legacy props) | Remove or check 4.3 release notes | EEVEE |145| ALL Grease Pencil API calls | Complete rewrite required | Grease Pencil |146147### 4.3 → 4.4 Replacements148149| Find | Replace With | Category |150|------|-------------|----------|151| `class MyOp(bpy.types.Operator):` (no super init) | Add `super().__init__(*args, **kwargs)` | Classes |152| `bpy.types.Sequence` | `bpy.types.Strip` | VSE |153| `action.fcurves.new(` (for new code) | Slotted Actions API | Animation |154| `paint.brush` (write access) | Read-only; use brush asset system | Paint |155156### 4.x → 5.0 Replacements157158| Find | Replace With | Category |159|------|-------------|----------|160| `import bgl` | `import gpu` | Drawing |161| `bgl.glEnable(bgl.GL_BLEND)` | `gpu.state.blend_set('ALPHA')` | Drawing |162| `bgl.glDisable(bgl.GL_BLEND)` | `gpu.state.blend_set('NONE')` | Drawing |163| `bgl.glLineWidth(w)` | `gpu.state.line_width_set(w)` | Drawing |164| `bgl.glEnable(bgl.GL_DEPTH_TEST)` | `gpu.state.depth_test_set('LESS_EQUAL')` | Drawing |165| `bgl.glDisable(bgl.GL_DEPTH_TEST)` | `gpu.state.depth_test_set('NONE')` | Drawing |166| `bgl.glPointSize(s)` | `gpu.state.point_size_set(s)` | Drawing |167| `bgl.glDepthMask(GL_TRUE)` | `gpu.state.depth_mask_set(True)` | Drawing |168| `image.gl_load()` / `image.bindcode` | `gpu.texture.from_image(image)` | Drawing |169| `gpu.types.GPUShader()` | `gpu.shader.create_from_info()` | Drawing |170| `scene['cycles']` | `scene.cycles.property_name` (attribute access) | Properties |171| `scene.node_tree` (compositor) | `scene.compositing_node_group` | Compositor |172| `scene.use_nodes` (compositor) | Remove (always active) | Compositor |173| `brush.sculpt_tool` | `brush.sculpt_brush_type` | Sculpt |174| `strip.end_frame` (VSE) | `strip.length` | VSE |175| `action.fcurves` (legacy) | Slotted Actions channelbag API | Animation |176| `BLENDER_EEVEE_NEXT` | `BLENDER_EEVEE` (reverted) | Render |177178### 5.0 → 5.1 Replacements179180| Find | Replace With | Category |181|------|-------------|----------|182| `sculpt.sample_color` | `paint.sample_color` | Sculpt |183| `frame_final_duration` | `duration` (deprecated, removed in 6.0) | VSE |184| `frame_final_start` | `left_handle` (deprecated, removed in 6.0) | VSE |185| `frame_final_end` | `right_handle` (deprecated, removed in 6.0) | VSE |186187---188189## 4. Decision Tree: Compatibility Shims vs Clean Migration190191```192MIGRATION STRATEGY DECISION193│194├── Target SINGLE Blender version?195│ └── YES → Clean migration: remove ALL legacy code, use target API only196│197├── Target VERSION RANGE (e.g., 3.x + 4.x)?198│ ├── Range spans 3.x and 4.0+ ?199│ │ └── YES → Use version-safe wrappers with bpy.app.version checks200│ ├── Range spans 4.x and 5.0+ ?201│ │ └── YES → Use version-safe wrappers; NEVER import bgl at module level202│ └── Range within single major (e.g., 4.0–4.4)?203│ └── Use hasattr() feature detection for minor version differences204│205└── Target LATEST only (drop legacy)?206 └── Clean migration: target version API only, no compatibility code207```208209### When to Use Compatibility Shims210211ALWAYS use compatibility shims when:212- The addon targets 2+ major Blender versions simultaneously213- The addon is distributed to users on different Blender versions214215NEVER use compatibility shims when:216- The migration targets a single Blender version217- The code is an internal tool pinned to one Blender version218219### Compatibility Module Pattern220221```python222# compat.py: Version-safe compatibility layer223import bpy224225BLENDER_4 = bpy.app.version >= (4, 0, 0)226BLENDER_41 = bpy.app.version >= (4, 1, 0)227BLENDER_42 = bpy.app.version >= (4, 2, 0)228BLENDER_43 = bpy.app.version >= (4, 3, 0)229BLENDER_44 = bpy.app.version >= (4, 4, 0)230BLENDER_5 = bpy.app.version >= (5, 0, 0)231BLENDER_51 = bpy.app.version >= (5, 1, 0)232233def apply_modifier(obj, modifier_name):234 if BLENDER_4:235 with bpy.context.temp_override(object=obj, active_object=obj):236 bpy.ops.object.modifier_apply(modifier=modifier_name)237 else:238 override = {"object": obj, "active_object": obj}239 bpy.ops.object.modifier_apply(override, modifier=modifier_name)240```241242### Conditional Import Pattern for BGL → gpu243244```python245# NEVER import bgl at module level in multi-version addons246import gpu247248# For addons targeting 3.x–4.x (before 5.0 removal):249try:250 import bgl251 HAS_BGL = True252except ImportError:253 HAS_BGL = False # Blender 5.0+254255# ALWAYS use gpu module for new code regardless of version256```257258---259260## 5. EEVEE Identifier Migration261262The EEVEE render engine identifier changed across versions. ALWAYS use this lookup:263264| Blender Version | EEVEE Identifier |265|----------------|-----------------|266| 3.x | `BLENDER_EEVEE` |267| 4.0 – 4.1 | `BLENDER_EEVEE` |268| 4.2 – 4.x | `BLENDER_EEVEE_NEXT` |269| 5.0+ | `BLENDER_EEVEE` |270271```python272def get_eevee_identifier():273 if bpy.app.version >= (5, 0, 0):274 return 'BLENDER_EEVEE'275 elif bpy.app.version >= (4, 2, 0):276 return 'BLENDER_EEVEE_NEXT'277 else:278 return 'BLENDER_EEVEE'279```280281---282283## 6. Extension System Migration (4.2+)284285When migrating from legacy addon to extension system:2862871. Create `blender_manifest.toml` in addon root2882. Remove `bl_info` dict from `__init__.py`2893. Set `blender_version_min = "4.2.0"` (or target minimum)2904. Add `id` matching the package directory name2915. Add `tagline` (max 64 chars, no trailing punctuation)2926. Add `type = "add-on"` or `type = "theme"`2937. Add network permissions if addon accesses internet:294 `[permissions]` → `network = "Reason for access"`2958. Add `license = ["SPDX:GPL-3.0-or-later"]` (or appropriate SPDX)296297---298299## 7. Critical Migration Rules3003011. NEVER use `import bgl` in code targeting Blender 5.0+ — the module is REMOVED.3022. ALWAYS restore `gpu.state` to defaults at the end of every draw callback.3033. NEVER pass a dict as the first argument to `bpy.ops` in Blender 4.0+ — use `context.temp_override()`.3044. ALWAYS add `super().__init__(*args, **kwargs)` to Blender type subclasses in 4.4+.3055. NEVER suppress `DeprecationWarning` — it hides migration signals.3066. NEVER use `try/except AttributeError` as a version check — use `bpy.app.version` or `hasattr()`.3077. ALWAYS set `POLYLINE_UNIFORM_COLOR` shader uniforms: `viewportSize` and `lineWidth`.3088. ALWAYS migrate Grease Pencil code completely when targeting 4.3+ — partial migration breaks.3099. NEVER assign embedded IDs to `PointerProperty` in 4.3+.31010. ALWAYS check the deprecation timeline in [blender-errors-version](../../errors/blender-errors-version/SKILL.md) before using any API.311312---313314## 8. Reference Links315316- **[references/methods.md](references/methods.md)** — Complete API migration mappings organized by version transition317- **[references/examples.md](references/examples.md)** — Before/after code examples for each migration path318- **[references/anti-patterns.md](references/anti-patterns.md)** — Common migration mistakes with explanations319320### Official Documentation321322- [Blender 4.0 Python API Changes](https://developer.blender.org/docs/release_notes/4.0/python_api/)323- [Blender 4.1 Python API Changes](https://developer.blender.org/docs/release_notes/4.1/python_api/)324- [Blender 4.2 Python API Changes](https://developer.blender.org/docs/release_notes/4.2/python_api/)325- [Blender 4.3 Python API Changes](https://developer.blender.org/docs/release_notes/4.3/python_api/)326- [Blender 4.4 Python API Changes](https://developer.blender.org/docs/release_notes/4.4/python_api/)327- [Blender 4.5 Python API Changes](https://developer.blender.org/docs/release_notes/4.5/python_api/)328- [Blender 5.0 Python API Changes](https://developer.blender.org/docs/release_notes/5.0/python_api/)329- [Blender 5.1 Python API Changes](https://developer.blender.org/docs/release_notes/5.1/python_api/)330- [Blender API Compatibility Notes](https://developer.blender.org/docs/release_notes/compatibility/)331- [Grease Pencil 4.3 Migration Guide](https://developer.blender.org/docs/release_notes/4.3/grease_pencil_migration/)