> For the complete documentation index, see [llms.txt](https://postica.gitbook.io/binding-system-3/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://postica.gitbook.io/binding-system-3/project-tools/diagnostics/validator.md).

# Bindings Validator

Every binding in the project, resolved and checked before you ship.

{% hint style="success" %}
**Learn by doing:** [Auditing a Project Before Release](/binding-system-3/tutorials/audit.md) runs this over a real project and works through what it finds.
{% endhint %}

A broken binding does not throw. It sits in a scene, correctly configured except for the one part that stopped being true, and does nothing until something reads it. In practice that means until somebody plays the scene it lives in, which on a large project can be weeks after the change that broke it.

The validator is the pass that finds those. It resolves every serialized binding the way the runtime would, and where the runtime would fail it says so, in words, with a route to the binding.

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2F21Qf752LfgnGaAdI0WZS%2FScreenshot%202026-09-27%20at%2011.48.26.png?alt=media&amp;token=dc555424-fb1f-4cef-b3d1-da3aff511569" alt=""><figcaption><p>A whole-project run, grouped by problem</p></figcaption></figure>

**Window ▸ Binding System ▸ Bindings Validator**

## What it is not

It is worth being clear about the boundary, because there are three project-wide tools in this section and they answer different questions.

<table><thead><tr><th width="290">Tool</th><th>What it answers</th></tr></thead><tbody><tr><td><a href="/binding-system-3/project-tools/diagnostics/dependencies.md">Bindings Dependencies</a></td><td><strong>What exists.</strong> Every binding in the project, grouped by source and path. Reports the structurally incomplete ones, the empty source and the empty path, because those are visible in the data itself.</td></tr><tr><td><strong>Bindings Validator</strong></td><td><strong>What is wrong.</strong> Walks each path against its source type, checks the direction against the member it lands on, and inspects the converters and modifiers in between. Finds the bindings that look complete and are not.</td></tr><tr><td><a href="/binding-system-3/project-tools/diagnostics/refactoring.md">Refactoring</a></td><td><strong>What to do about a rename.</strong> Takes the members the validator found missing and redirects every binding of each, in one decision per member.</td></tr></tbody></table>

The validator finds a binding whose path stopped resolving three components deep. The dependencies window cannot: from the data alone, that binding looks perfectly configured.

Neither reports a *wrong value*. A binding pointing confidently at the wrong member is valid. For that, [Live Debug](/binding-system-3/project-tools/diagnostics/live-debug.md) at runtime or [Path Value Preview](/binding-system-3/project-tools/diagnostics/path-value-preview.md) while authoring.

## Choosing a scope

The dropdown in the header picks how much is looked at.

<table><thead><tr><th width="230">Scope</th><th>What it covers</th></tr></thead><tbody><tr><td><strong>Active Scene</strong></td><td>The scene you are working in. Fast, and the one to reach for while building something.</td></tr><tr><td><strong>Open Scenes</strong></td><td>Every scene currently open, which for a multi-scene setup is the useful unit.</td></tr><tr><td><strong>Build Scenes</strong></td><td>Every scene listed in build settings, open or not. The question "does what ships work".</td></tr><tr><td><strong>Whole Project</strong></td><td>Every scene, prefab and asset. The release pass.</td></tr><tr><td><strong>Selection</strong></td><td>What is selected in the Hierarchy or the Project window. Folders contribute everything inside them, so one feature folder is one scope.</td></tr></tbody></table>

**Selection** is also reachable without opening the window first: right click a GameObject, a component header or an asset and choose **Binding ▸ Validate Bindings**.

## Running it

Press **Validate**. The button becomes **Stop** and a progress bar appears.

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2FoJXvmtZVnM7nfknlcjcP%2FScreenshot%202026-09-27%20at%2011.50.41.png?alt=media&amp;token=a8010b52-ea21-42fa-91d4-3397a876a8e5" alt=""><figcaption><p>A whole-project run in progress, with the asset it is on and a stop button</p></figcaption></figure>

A whole-project run is deliberately slow, because it loads things. It is also interruptible at any point, and stopping produces a report of everything reached so far, marked as incomplete rather than pretending to be a clean bill of health.

The run happens in three phases, which is why the progress bar moves the way it does:

1. **Collecting.** The asset list, on the main thread. Brief.
2. **Looking for bindings.** Every candidate file is read on background threads and checked for the marker every serialized binding carries. Most assets in a project have no binding in them at all and are dropped here without ever being loaded. This is what makes a whole-project run affordable; the status bar reports how many were skipped.
3. **Validating.** Only the files that survived are loaded and walked, a few per frame, so the editor stays usable.

### It writes nothing

A validation reads. It does not save a scene, mark one dirty, or write a single asset, and it never asks permission to start because there is nothing to ask about.

That is not just tidiness. Writing an asset runs the import pipeline, an import picks up any script edit sitting on disk, and the compilation that follows ends in a domain reload, which would take the run and everything it had found with it. A tool whose job is to look at a project has no business restarting the editor.

The same care applies to the scenes it opens:

<table><thead><tr><th width="290">The scene is</th><th>What the validator does</th></tr></thead><tbody><tr><td>Open and loaded</td><td>Read where it is. Your unsaved edits are read as you see them, which is the point.</td></tr><tr><td>In the setup but unloaded</td><td>Loaded, read, and unloaded again. It stays in the setup.</td></tr><tr><td>Not open at all</td><td>Opened additively, read, and removed again.</td></tr></tbody></table>

Because nothing already open is ever unloaded, no unsaved work is ever at risk.

While a run is going, Unity's automatic asset refresh is held back, so a script you edit in the middle of a pass compiles when it finishes rather than ending it. If a reload happens anyway, for a reason outside the validator, the run takes itself up again on the other side and the result it had is kept.

### Scenes that are not open

A scene has to be open for its bindings to be inspected properly, because a converter or a modifier is a `[SerializeReference]` slot whose contents only exist once Unity has deserialized them.

So by default the validator **opens closed scenes one at a time**, validates them, and puts them back exactly as it found them, whether it finishes or you stop it.

{% hint style="info" %}
If you would rather it did not touch your scenes, turn off **Open closed scenes** in the [options panel](#choosing-what-to-check). Those scenes are then read from their files instead. Broken paths, missing types and direction conflicts are still found exactly, because everything a path needs is written in the file; converters, modifiers and object identity are not looked at. Findings from such a scene say so, both on the row and in the detail panel, and the status bar counts them.
{% endhint %}

In play mode the validator never opens scenes, whatever the setting says. Nothing can be written while the game runs, and the objects in front of it are clones rather than the assets themselves.

## Reading the result

### The summary

Three chips: errors, warnings and hints. Each is also a filter; click one to hide that severity.

<table><thead><tr><th width="180">Severity</th><th>What it means</th></tr></thead><tbody><tr><td><strong>Error</strong></td><td>The binding cannot work as configured. Something will silently do nothing in the build.</td></tr><tr><td><strong>Warning</strong></td><td>It works, but probably not the way it was meant to. An empty modifier slot, a modifier that never runs, a bind variable nothing declares.</td></tr><tr><td><strong>Hint</strong></td><td>Never wrong, worth knowing. A long path walked every frame, a method called on every update.</td></tr></tbody></table>

The right-hand chip counts what was actually checked, and the status bar at the bottom carries the rest: how long it took, how many assets were skipped for holding no bindings, how many scenes were read from file only, and whether the run was stopped early.

### The grouping

By default findings are grouped **by problem**, not by asset. One member renamed breaks a lot of bindings, and reading the same sentence forty times helps nobody: the group header states the problem once and carries the count. Click a header to collapse it.

The **Group** dropdown offers the other useful orderings: **by asset** for a per-scene sweep, **by severity** when you only want to see errors first, and **none** for a flat list.

### The detail panel

Select a row.

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2FaYxZv6LoNrpWRdmmPywj%2FScreenshot%202026-09-28%20at%2018.10.50.png?alt=media&amp;token=2959b87f-7a16-4f9c-b319-b42c52f0757c" alt=""><figcaption><p>A broken path, with the segment that stopped the walk marked</p></figcaption></figure>

<table><thead><tr><th width="230">Part</th><th>What it shows</th></tr></thead><tbody><tr><td>The explanation</td><td>What is wrong, naming the types and members involved.</td></tr><tr><td>The suggestion</td><td>What can be done about it, in one sentence.</td></tr><tr><td><strong>Binding</strong></td><td>The source type, the direction, and the path broken into its segments. <strong>The segment that stopped the walk is highlighted</strong>, which for a long path is the whole answer.</td></tr><tr><td><strong>Where</strong></td><td>Asset, object, component and field. Enough to find it by hand if you would rather.</td></tr></tbody></table>

**Show me** selects the object and highlights the bound field, opening its scene when needed. That is the button to use; the **Where** block is there so you can also just read it.

## Repairing

Each finding offers the repairs that fit it, and only those. Anything that would have to guess what you meant is not offered here; it goes to [Refactoring](/binding-system-3/project-tools/diagnostics/refactoring.md), which asks first.

<table><thead><tr><th width="230">Repair</th><th>What it does</th></tr></thead><tbody><tr><td><strong>Switch off</strong></td><td>Turns the binding off. The field keeps the literal value it already holds, so nothing is lost. Offered for every finding where the binding cannot work at all.</td></tr><tr><td><strong>Make read only</strong> / <strong>Make write only</strong></td><td>Narrows the direction to the one the member actually supports.</td></tr><tr><td><strong>Remove modifier</strong></td><td>Deletes the exact slot the finding is about.</td></tr><tr><td><strong>Remove converter</strong></td><td>Clears a converter slot whose type is gone.</td></tr><tr><td><strong>Clear source</strong></td><td>Removes a reference that cannot be serialized, keeping the path.</td></tr><tr><td><strong>Delete binding</strong></td><td>Removes a proxy binding entirely.</td></tr></tbody></table>

Every repair goes through undo and marks the right scene or asset dirty.

### Repair safe findings

The toolbar button applies, in one undo step, every repair that **cannot lose any of your work**: switching off a binding that could never resolve, removing an empty modifier slot, narrowing a direction that was never going to work. You are told how many before anything happens.

Repairs that are destructive are deliberately excluded from it. Clearing a source loses the reference; deleting a proxy loses the binding. Those stay one click at a time.

### Refactor renamed members

The other toolbar button collects every finding about a member that was **renamed or moved**, hands them all to the [Refactoring](/binding-system-3/project-tools/diagnostics/refactoring.md) window and opens it. One decision per member then redirects all of their bindings at once, including the ones in scenes that were never opened.

This is the pairing that matters after a big rename: the validator finds them, the refactor window fixes them.

## Choosing what to check

The button beside the scope dropdown slides a panel in over the results.

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2FAPvVTUAnkRepQ8hJLSEy%2FScreenshot%202026-09-28%20at%2018.11.34.png?alt=media&amp;token=7da62758-e04a-4f1f-bd14-5f807d93bfeb" alt=""><figcaption><p>The options panel, sliding in over the findings</p></figcaption></figure>

Each group of checks is a toggle with a sentence under it saying what it costs you to turn it off. Everything except **Correctness** is optional: a healthy project can still be full of hints, and a pass that only asks "does this work" is a legitimate thing to want.

**Performance** carries one number as well as a toggle, **Deep path after**, which is how many segments a per-frame binding may walk before it earns a hint. Five by default.

**Precision** holds **Open closed scenes**, described [above](#scenes-that-are-not-open).

Nothing here re-runs anything. Options apply to the next run, and the panel says so; once you have changed one with a report on screen it says something firmer, because findings from a previous run are not an answer to the question you just asked. **Validate again** at the bottom closes the panel and re-runs the current scope.

Your choices are remembered between sessions.

## What it checks

### Source

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>No source assigned</em></td><td>Switched on, needs a directly assigned source, and has none.</td></tr><tr><td><em>Source object is missing</em></td><td>The reference points at an object that is no longer in the project. Different from never having assigned one, and worth telling apart.</td></tr><tr><td><em>Source type no longer exists</em></td><td>The script the binding was saved against was deleted, renamed or moved to another assembly.</td></tr><tr><td><em>Source is not of the expected type</em></td><td>The path was authored against one type and the assigned object is another.</td></tr><tr><td><em>Source lookup key is empty</em></td><td>A dynamic <a href="/binding-system-3/overview/sources.md">source mode</a> is configured with nothing to look the source up by.</td></tr><tr><td><em>The type this binding carries no longer exists</em></td><td>The binding's own declared type is gone, whatever its path does.</td></tr></tbody></table>

### Path

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>Nothing to bind to</em></td><td>Switched on with no path picked.</td></tr><tr><td><em>Path no longer exists</em></td><td>A segment names a member that is gone. The finding says which segment, and on which type. This is the one <a href="/binding-system-3/project-tools/diagnostics/refactoring.md">Refactoring</a> exists for.</td></tr><tr><td><em>Method no longer exists</em></td><td>Same, for a method whose name or parameter list changed.</td></tr><tr><td><em>Accessor provider is not registered</em></td><td>The path goes through an <a href="/binding-system-3/reference/extending/accessor-providers.md">accessor provider</a> that nothing registers any more.</td></tr><tr><td><em>Cast can never succeed</em> / <em>Cast target type no longer exists</em></td><td>A cast segment naming a type the value can never hold, or one that has been deleted.</td></tr><tr><td><em>Bind variable is not declared</em></td><td>No <a href="/binding-system-3/overview/bind-variables.md">variable</a> by that name is registered. A warning rather than an error, because a variable can be registered from code and only exist once the game runs.</td></tr></tbody></table>

### Read and write direction

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>Member cannot be written</em></td><td>The binding writes to something with no setter.</td></tr><tr><td><em>Member cannot be read</em></td><td>The binding reads something write only.</td></tr><tr><td><em>Write cannot reach the target</em></td><td>The subtle one. Writing <code>transform.localPosition.x</code> means writing the whole <code>Vector3</code> back into <code>localPosition</code>. If a value type halfway down the chain has no setter, the write cannot get home, even though the member at the end is perfectly writable.</td></tr></tbody></table>

### Type conversion

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>No read conversion available</em></td><td>The path produces one type, the binding carries another, and nothing converts between them. Checked separately for each direction, because a read/write binding needs both.</td></tr><tr><td><em>The read converter does not fit</em></td><td>A <a href="/binding-system-3/pipeline/converters.md">converter</a> is set but its two ends do not match the types it sits between.</td></tr><tr><td><em>Read converter type is missing</em></td><td>A converter was saved and its class is no longer in the project.</td></tr></tbody></table>

### Modifiers

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>Modifier slot is empty</em></td><td>A slot holding nothing. Skipped at runtime, so a warning.</td></tr><tr><td><em>Modifier type is missing</em></td><td>A <a href="/binding-system-3/pipeline/modifiers.md">modifier</a> was saved and its class is gone. This is the case <a href="/binding-system-3/tutorials/surviving-a-rename.md#what-it-cannot-repair">renaming a modifier class</a> produces, and it is the only tool that reports it.</td></tr><tr><td><em>Modifier cannot handle this value</em></td><td>Applied to a type it does not accept.</td></tr><tr><td><em>Modifier never runs</em></td><td>A read-only modifier on a write-only binding, or the other way round.</td></tr></tbody></table>

### Cross scene references

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>Binding crosses two scenes</em></td><td>Unity does not serialize references across scenes, so this one is lost on save.</td></tr><tr><td><em>Asset binding points at a scene object</em></td><td>Same problem in the other direction: a prefab or asset referring to something that only exists in a scene.</td></tr></tbody></table>

### Proxy bindings

A [proxy binding](/binding-system-3/overview/proxy-bindings.md) has two ends, the member it writes into and the value it reads, and they break independently. Every check above applies to the value end. These apply to the other one, or to the pair.

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>Proxy binding has no target</em></td><td>It points at no object at all. It does nothing at runtime.</td></tr><tr><td><em>Proxy binding has no target member</em></td><td>It points at an object but names no member on it.</td></tr><tr><td><em>Proxy binding target no longer exists</em></td><td>The member it writes into was renamed, moved or deleted. This one goes to <a href="/binding-system-3/project-tools/diagnostics/refactoring.md">Refactoring</a> like any other missing member.</td></tr><tr><td><em>Proxy binding was never finished</em></td><td>Switched off, with no source and no path. This is the state <strong>Enable Binding</strong> leaves behind when somebody adds a binding to a field and walks away, and nothing else reports it, because from the outside it looks exactly like a field with no binding at all.</td></tr><tr><td><em>Proxy binding has no value to read</em></td><td>The same emptiness, but switched on. It runs every frame and writes nothing.</td></tr><tr><td><em>Two proxy bindings drive the same target</em></td><td>The container looks its proxies up by target, so only one of the two is reachable, and which one depends on load order.</td></tr><tr><td><em>Proxy binding reads what it writes</em></td><td>A proxy taking its value from the very member it writes into. At best a no-op, at worst a feedback loop through the modifier chain.</td></tr><tr><td><em>Empty proxy binding slot</em></td><td>The list holds a slot with nothing in it.</td></tr></tbody></table>

{% hint style="info" %}
A proxy finding is only ever offered one repair, **Delete binding**, and it is never counted as safe. An ordinary binding can be switched off and keep the literal value the field already holds; a proxy has no value of its own, so there is nothing to fall back to and removing it is the only honest option. Both ends are checked on proxies held by a `ProxyBindings` component in a scene and by a **Proxy Bindings Asset** in the project.
{% endhint %}

### Performance hints

<table><thead><tr><th width="290">Finding</th><th>What it means</th></tr></thead><tbody><tr><td><em>Long path evaluated every frame</em></td><td>A per-frame binding walking five or more members.</td></tr><tr><td><em>Method called every frame</em></td><td>A per-frame binding whose path goes through a method call.</td></tr></tbody></table>

Neither is wrong. Both are worth a look with the [Bindings Monitor](/binding-system-3/project-tools/diagnostics/bindings-monitor.md), which is the tool that actually measures cost.

## Exporting a report

The **Report** menu copies the findings to the clipboard, prints them to the console, or saves them as a text file. The report is grouped by asset, so it reads as a to-do list rather than a log, and it carries the counts, the timing and the note about scenes read from file only.

## Running it on a build machine

A broken binding is exactly the kind of mistake continuous integration should catch, so the whole validator is reachable without a window:

```bash
Unity -batchmode -quit -projectPath <project> \
      -executeMethod Postica.BindingSystem.Validation.BindValidationBatch.ValidateProject
```

`ValidateBuildScenes` is the narrower entry point, covering only what is in build settings.

<table><thead><tr><th width="290">Argument</th><th>What it does</th></tr></thead><tbody><tr><td><code>-bindValidationStrict</code></td><td>Fail on warnings as well as errors.</td></tr><tr><td><code>-bindValidationReport &#x3C;path></code></td><td>Write the full report to a file as well as to the log.</td></tr></tbody></table>

The process exits with `0` when nothing is wrong and `1` when there are errors, or when the run did not finish. Progress is written to the log every ten percent.

## Nothing found

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2FAb221IBH0vWqaNrlRypd%2FScreenshot%202026-09-28%20at%2018.14.28.png?alt=media&amp;token=a0d053bf-1228-4d1f-bbbd-e8992eaa18b0" alt=""><figcaption><p>A scope with nothing wrong in it</p></figcaption></figure>

An empty list is four different results, and the window tells them apart rather than showing one green tick for all of them. This matters more than it sounds: "nothing is wrong" and "nothing was looked at" produce exactly the same list.

<table><thead><tr><th width="250">What it says</th><th>What it means</th></tr></thead><tbody><tr><td><strong>No problems found</strong></td><td>The real one. It carries how many bindings were checked and across how many assets, which is the number that makes it trustworthy.</td></tr><tr><td><strong>No bindings here</strong></td><td>Assets were opened and none of them holds a binding. Nothing was validated because there was nothing to validate.</td></tr><tr><td><strong>Nothing was scanned</strong></td><td>The scope itself was empty. <strong>Selection</strong> needs something selected, and <strong>Active Scene</strong> needs a scene that has been saved at least once.</td></tr><tr><td><strong>Stopped early</strong></td><td>You pressed <strong>Stop</strong>. Whatever was reached is reported, and the result is explicitly not the whole picture.</td></tr></tbody></table>

### Not everything was read

One more, and it is the one to report rather than to act on. Every candidate file is checked for bind data twice, once as text during the scan and again as objects while validating, and the two disagreeing means the second pass has a blind spot.

When that happens the window says so instead of claiming a clean result, and the status bar counts the assets involved. Save the report to see which ones they are, and send it on.

## Settings

**Project Settings ▸ Binding System ▸ Configuration ▸ Bindings Validator** carries the same two entry points, **Validate Active Scene** and **Validate Project**, so a pass can be started from the place where the rest of the project's binding configuration lives.

## Related pages

* [Refactoring](/binding-system-3/project-tools/diagnostics/refactoring.md): what to do about the members it found missing.
* [Bindings Dependencies](/binding-system-3/project-tools/diagnostics/dependencies.md): what exists, rather than what is wrong.
* [Error Visualization](/binding-system-3/project-tools/diagnostics/errors.md): the same problems, reported on the row while you author.
* [Bindings Monitor](/binding-system-3/project-tools/diagnostics/bindings-monitor.md): the tool that measures cost, for the performance hints.
* [Auditing a Project Before Release](/binding-system-3/tutorials/audit.md): the full pass, in order.
