DOCUMENTATION

Silent damage

Calls that answered success and still did harm, or whose success report described the request instead of the result. Read it before you trust a green response, or when a scene or asset is not what a call said it left.

The pattern in every case is the same: the answer looked fine, and only a second look through a different call showed the damage. Each note says what you would see, what was really happening, what to do, and whether the current build protects you. For the safety gate that stops the broadest class of writes, see Safety and write modes. For recovering work, see Losing work.

A value that was understood differently than it was sent

A number that was never a number

What you see. set_property (or override_inherited_component) with "SphereRadius": "not-a-float" answers ok: true, applied: true, with wanted: "0.000000". The radius is now 0, and the read-back matches.

What is happening. The engine's text importers stop at the first character they cannot read and report success for what they consumed. A float with nothing readable becomes 0. An unrecognized bool word becomes false, an unrecognized enum name becomes the first entry, and an object path that does not resolve is stored as null. The old check compared the value after the write with the value the same importer had produced, so both sides agreed on the wrong answer.

What to do. Send values in their typed form, and read the property back through a call that does not share the parse. A write that landed is not a value that was understood.

Does the current build protect you. Yes. The text is checked against the property's type before anything is imported: numbers must parse whole ("12abc" and exponent notation are refused), bools must be a recognized literal, enums must name a real entry with the valid ones listed, and hard object references must resolve or be an explicit None. Where a kind cannot be pre-checked (a struct or container passed as export text) the response says so in typeValidated and typeValidationNote.

A failed set_property that wiped the old value

What you see. A struct literal with a typo fails, and the property is now empty instead of unchanged.

What is happening. The engine parses text into the property's own memory. It can clear the destination before it decides the text is invalid.

Does the current build protect you. Yes. The text is parsed into a scratch copy seeded from the current value, and published only on success. Listeners are told of a change only when one happened.

A mandatory class that quietly became "self"

What you see. add_cast, add_spawn_actor, add_create_widget or add_tree_widget called with a misspelled class key compiles clean and succeeds. A cast of a Blueprint to itself, or a spawn of a copy of itself, is the result.

What is happening. An empty class name means "this Blueprint" for calls where that is a feature, and the mandatory-class calls shared the helper. A missing key looked like a deliberate self reference, and the type check that followed passed because the Blueprint is itself an actor or a widget.

Does the current build protect you. Yes. An empty class on a call that requires one is refused naming the parameter, and the common alternate spellings are accepted.

A blueprint type that fell through to "normal"

What you see. create_blueprint with blueprintType: "Widget" (instead of WidgetBlueprint) produces an asset that looks fine in the Content Browser, has no widget tree and no designer tab, and fails every widget call afterward.

What is happening. Any unrecognized type string fell through to an ordinary Blueprint parented to the widget base class.

Does the current build protect you. Yes. An unknown blueprintType is refused with the valid values, and the Widget against WidgetBlueprint mix-up is named.

A failed call that changed something anyway

A cancelled transaction undoes nothing

What you see. override_inherited_component with a bad property value correctly answers ok: false, and a later get_inherited_component reports overrideExists: true. The child Blueprint now shadows its parent for that component, with no undo step.

What is happening. The bridge wraps each mutating call in an undo step and cancels it on failure. A cancel discards the record; it does not revert anything. A call that creates something and then validates leaves the creation behind.

What to do. After any failed write, read the target back before retrying. Do not assume a failed call changed nothing.

Does the current build protect you. For the calls known to be affected, yes: they check first and create second, and a refusal says nothingModified: true. That covers override_inherited_component, add_component, add_foliage_instances, add_timeline and create_material_instance. A few calls cannot check first (add_pin, create_struct, set_variable_flags), and their failure text names exactly what is left behind and how to remove it.

Deleting "what is at" a path

What you see. After a cleanup deleted a material at a stale path, every reference to it was empty: 33 level actors had lost their override materials, and the level had been saved.

What is happening. The stale path held a redirector. Deleting a redirector cuts every saved reference that still names its path, so those references load as nothing the next time the level opens.

Does the current build protect you. Yes. delete_asset checks for a redirector before anything is loaded, refuses it by name with redirectsTo, and points at fix_up_redirectors, which repoints the referencers first. redirector: true deletes the redirector itself, and only when nothing references it.

A sweeping save that reached into the engine install

What you see. After a run of recorded tests, a stock engine content file, a tutorial skeleton, carried the run's timestamps in two engine installs. Every later project on those installs loaded the changed file.

What is happening. save_dirty_packages with no filter saved every dirty package, and engine content is none of the things the save skipped by default. In full write mode the protection for /Engine content is lifted on purpose.

What to do. If engine content was written, restore the original with the launcher's Verify. Name the packages you mean to save.

Does the current build protect you. Yes. save_dirty_packages skips any package whose file is inside the engine directory, in every mode, and lists it in skipped. save_package with that path still writes it deliberately.

Writes that landed and were reported as failing, or the reverse

  • A collection write reported as zero. Adding a class path to a collection answered ok: false ("0 of 1 path(s) are in the collection afterwards") while the engine had written it to disk. The engine's reader for assets skips class paths, and the check used that reader. Success is now decided from the full set, and describe_collection lists every path, with classes naming the class paths.
  • deletedSources: true for a source still on disk. consolidate_assets echoed the caller's deleteSources request. Consolidating leaves a redirector in the source's package, which is not deletion. The answer now reports sourcesAfter (gone, redirector or still there), keeps the request under deleteSourcesRequested, sets deletedSources only when every source is really gone, and points at fix_up_redirectors to finish.
  • A refused struct default that had been accepted. add_struct_member warned that 1.5 was refused for a float member that held it as 1.500000. Defaults are now compared as numbers when both parse as numbers.
  • A typo that became a real layer. modify_actor_layers creates a layer for any new name, as the Outliner does, so ["Props", "Prpos"] made an empty permanent layer and said so only as one boolean. The answer now returns layersCreated, the list of names that were new, with a note.

Edits that vanish later

Landscape edits discarded by the next composite

What you see. sculpt_landscape, paint_landscape or import_landscape_heightmap answered ok: true, and the import added "verified". Later the edit was gone: after one apply_spline_to_landscape far from the sculpt, five witness points went back to flat and the sculpt center went from 4245.3 to 0.0.

What is happening. On a landscape with edit layers (every landscape on UE 5.7 and later, and any made in the editor on 5.3 to 5.6), the merged heightmap is an output that is rebuilt from the layers at each composite. The writers wrote the merged result, which lasted until something asked for a composite. Waiting does not show this; only triggering a composite does.

Does the current build protect you. Yes. The three writers write through a layer. They accept editLayer, defaulting to the first layer, and refuse an unknown or locked layer before anything is read. Import re-reads the surface after the composite and reports surfaceDiffering. sample_landscape reads the surface or a named layer.

Several objects sent, only the first one kept

What you see. A send from Blender to Unreal of four objects replaced an earlier send of the same asset. The file held all four, and the asset came back with one section and the first object's vertex count only. The read-back failed on a bounding box, and the message that the asset was kept read as though it were fine.

What is happening. An import over an existing static mesh is a re-import. The first replace converted the asset to the engine's newer importer, and the next replace ran the older importer with default options, whose "combine meshes" setting is off. It took the first mesh node. A test that sent one object, or replaced once, could not see it.

What to do. When a replaced asset's triangle or section count differs from the file's, treat it as failed even if the call answered ok.

Does the current build protect you. Yes. import_asset gives a replaced mesh this import's options first and reports replacedImportData. The send's read-back counts triangles and sections against the materials the faces use, names the objects that did not arrive, and a replaced asset that fails says written.matchesSend: false.

Reads that were wrong

  • Zero nodes for every MetaSound on UE 5.5 and later. describe_metasound answered nodeCount: 0 and edgeCount: 0 for stock sources that hold 31 nodes and 37 edges. From 5.5 the engine keeps the graph in pages and leaves the old field present but emptied. The count now comes from whichever field exists on that engine, reports pageCount and pages, and is left absent with a note when it cannot be read. A zero is only visibly wrong against content you know has something.
  • Captures showing a second copy of an animated object. With a Sequencer open, capture_camera on a sequence drew the Sequencer's own copy of a spawnable beside the capture's copy, so a decal keyed hidden showed fully revealed. Movie renders after many sequence edits showed stale values for the same reason. capture_camera now hides copies only an open Sequencer binds, and render_sequence refuses while a Sequencer is open unless openSequencers is close or keep. If a render does not match the editor view, close Sequencer tabs first.
  • Terrain black, and every building white. Binding a runtime virtual texture that has no valid pages does not repair terrain; it turns every other material that samples that texture blown-out white. Spawn a known-good reference object early (a basic-shapes cube with its stock material) to tell a scene problem from a material problem. Do not bind a shipped virtual texture into a scratch level.

Memory that does not come back

What you see. About 1,800 capture_camera calls at 2560x1440 for one clip raised the editor's memory from 5.8 GB to 33.1 GB, about 14.9 MB per call. The next clip ran out of memory at 56.3 GB.

What is happening. Each call made a render target and a temporary capture actor and left both for garbage collection. Outside Play-In-Editor the editor world never begins play, so nothing periodic collects anything.

Does the current build protect you. Yes. The render target is released inside the call once the pixels are read back.