Logging & validation
Saneject injection is designed to finish a full run and then report everything it found.
Instead of stopping at the first failure, one run gives you a complete list of issues to fix: invalid bindingsBindingInstruction declared in a Scope that tells Saneject what to resolve, how to inject it, and where to search., missing bindingsBindingInstruction declared in a Scope that tells Saneject what to resolve, how to inject it, and where to search., missing dependencies, unused bindingsBindingInstruction declared in a Scope that tells Saneject what to resolve, how to inject it, and where to search., and summary totals.
Log examples by type
❌ Error examples
❌ Saneject: Invalid binding [Binding: BindAsset<PlayerController>() | Scope: GameplayScope] Asset binding type 'PlayerController' derives from Component.
❌ Saneject: Missing binding. Expected something like [Binding: BindComponent<IHealthService>() | Nearest scope: GameplayScope] [Field: Root/Player/PlayerHud/PlayerHud.healthService]
❌ Saneject: Method invocation exception (exception details in next log) [Method: Root/Player/PlayerBootstrap/PlayerBootstrap.Initialize]
⚠️ Warning examples
⚠️ Saneject: Unused binding [Binding: BindComponent<AudioSource>().FromDescendants() | Scope: GameplayScope]. If you don't plan to use this binding, you can safely remove it.
⚠️ 2 missing binding/dependency errors were suppressed due to [Inject(suppressMissingErrors: true)]. Remove the flag to view detailed logs.
ℹ️ Info examples
ℹ️ Saneject: Created proxy asset at 'Assets/SanejectGenerated/RuntimeProxies/EnemyServiceProxy.asset'.
ℹ️ Saneject: Injection complete | 3 scopes processed | 6 globals registered | 1 proxy swap target registered | 24 fields injected | 3 properties injected | 2 methods injected | 0 missing dependencies | 0 missing bindings | 0 invalid bindings | 1 unused binding | Completed in 18 ms
Where validation and logging happen in the pipeline
For each run, Saneject executes these stages in order:
- Build the injection graphInjection graphHierarchy model built from selected root hierarchies. It contains transform, component, scope, binding, and injection-member information used by the edit-time pipeline. and apply
ContextWalkFilter. - Build an
InjectionContext(active transforms, components, scopesScopeMonoBehaviourthat declares bindings for a part of your hierarchy., and bindingsBindingInstruction declared in aScopethat tells Saneject what to resolve, how to inject it, and where to search.). - Validate bindingsBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search.. - Resolve globals, fields/properties, and methods.
- Inject resolved values.
- Collect proxy swap targetsProxy swap targetComponent that implements
IRuntimeProxySwapTargetand is asked at runtime startup to replace proxy references with resolved real instances.. - Log errors, warnings, and summary.
Validation runs before resolution. Invalid bindingsBindingInstruction declared in a Scope that tells Saneject what to resolve, how to inject it, and where to search. are reported and then excluded from resolution for that run.
Binding validation
Validation checks each active bindingBindingInstruction declared in a Scope that tells Saneject what to resolve, how to inject it, and where to search. and records all failures without aborting the run.
Saneject validates:
- Duplicate bindingBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search. declarations inside the same scopeScopeMonoBehaviourthat declares bindings for a part of your hierarchy.. - Duplicate global bindingsBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search. for the same concrete type across scopesScopeMonoBehaviourthat declares bindings for a part of your hierarchy.. - Runtime proxy bindingRuntime proxy bindingComponent binding configured with
FromRuntimeProxy()that injects a proxy asset at editor time and swaps it for a real runtime instance during scope initialization. requirements (interfaceandconcretetype required, and single-value only). - Component bindingComponent bindingBinding declared with
BindComponent...orBindComponents...that resolvesComponentinstances from transforms, hierarchies, scenes, or explicit instances. concrete type must derive fromUnityEngine.Component. - Asset bindingAsset bindingBinding declared with
BindAsset...orBindAssets...that resolvesUnityEngine.Objectassets from project content instead of scene or hierarchy components. concrete type must not derive fromUnityEngine.Component. InterfaceTypemust be an interface.- Concrete type must implement the interface when both are specified.
- A locator strategyLocator strategyThe
From...part of a binding that defines where candidates are searched or loaded from. must be specified (From...).
If a bindingBindingInstruction declared in a Scope that tells Saneject what to resolve, how to inject it, and where to search. fails validation:
- It is logged as an
Invalid bindingerror. - It is excluded from
ValidBindingNodes. - The rest of injection still continues.
What gets logged in a single run
A single run can emit:
- Error logs: invalid bindingBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search., missing bindingBindingInstruction declared in aScopethat tells Saneject what to resolve, how to inject it, and where to search., missing global object, missing dependency, binding filterBinding filterPredicate declared withWhere...methods that removes candidates after location and before injection. exception, method invocation exception. - Warning logs: unused bindingsBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search.. - Info logs: created runtime proxyRuntime proxy
ScriptableObjectplaceholder asset (RuntimeProxy<TComponent>) injected into interface members at editor time and swapped to the real instance during scope startup. assets and summary logs (if enabled).
Summary behavior:
- Controlled by
Saneject/Settings/User Settings/Log Injection Summary. - Severity is computed from unsuppressed counts:
Errorif there are missing binding/dependency/invalid bindingBindingInstruction declared in aScopethat tells Saneject what to resolve, how to inject it, and where to search. errors.Warningif there are no such errors but there are unused bindingsBindingInstruction declared in aScopethat tells Saneject what to resolve, how to inject it, and where to search..Infootherwise.
- If
[Inject(suppressMissingErrors: true)]suppresses missing binding/dependency errors, the summary appends a warning line with the suppressed count.
No-scope behavior:
- If zero scopesScope
MonoBehaviourthat declares bindings for a part of your hierarchy. are processed, Saneject logs that no scopesScopeMonoBehaviourthat declares bindings for a part of your hierarchy. were found and nothing was injected.
What gets logged in batch runs
Batch injectionBatch injectionRunning injection across multiple selected scene and prefab assets in one operation. adds structure around normal per-asset logs:
- Batch header for scenes and/or prefabs.
- Per-asset start/end markers.
- Normal injection logs inside each asset section, including per-asset summary.
- Final batch summary section with aggregated scene and prefab summaries.
If the save-scenes prompt is cancelled before batch starts, Saneject logs Injection cancelled by user. and exits without injecting.
Exceptions and flow continuity
Exceptions from user code inside [Inject] methods are caught and logged as Method invocation exception, followed by the exception details in a separate log entry.
This keeps the injection runInjection runOne execution of the injection pipeline for a chosen selection, context walk filter, and active set of targets. moving so later targets can still be processed.
Exceptions thrown by bindingBindingInstruction declared in a Scope that tells Saneject what to resolve, how to inject it, and where to search. dependency filters are also caught and logged as Binding filter exception, so one faulty filter does not terminate the run.
Filter logs from scopes and components
Saneject adds contextContextSerialization boundary Saneject uses during injection to decide scope traversal and candidate eligibility. menu actions that set the Unity Console search query:
- On
Scope:Saneject/Filter Logs By Scope Type- Sets search to
Scope: <ScopeTypeName>.
- Sets search to
- On
ScopeandMonoBehaviour:Saneject/Filter Logs By Component Path- Sets search to the full component pathComponent pathFull hierarchy path plus component type name, for example
Root/Player/HUD/HudController. Used by Saneject log filtering and context menu actions., for exampleRoot/Player/Hud/PlayerHud.
- Sets search to the full component pathComponent pathFull hierarchy path plus component type name, for example
These filters work well because Saneject log signatures include scopeScopeMonoBehaviour that declares bindings for a part of your hierarchy. names and full member/component paths.
Settings that affect logging
In Saneject/Settings/User Settings:
Clear Logs On Injection: clears the console before injection starts.Log Injection Summary: enables/disables summary logging.Log Unused Bindings: enables/disables unused bindingBindingInstruction declared in aScopethat tells Saneject what to resolve, how to inject it, and where to search. warnings.
In Saneject/Settings/Project Settings:
Use Context Isolation: when enabled, cross-context candidates are rejected during resolution. This can surface missing dependency errors that include rejected candidate type details.