Unity-Technologies/UnityCsReference · error · InvalidOperationException
Cannot call ReportAssetChanged outside of the OnAssetsModifi
Error message
Cannot call ReportAssetChanged outside of the OnAssetsModified callback
What it means
AssetsModifiedProcessor.ReportAssetChanged is only valid inside the OnAssetsModified virtual callback. The protected method throws InvalidOperationException when assetsReportedChanged is null, because Unity sets that HashSet only during the callback dispatch — calling ReportAssetChanged at any other time means you are outside the valid window. The null check on the property is the sentinel for 'not currently in callback context.'
Source
Thrown at Editor/Mono/AssetDatabase/AssetsModifiedProcessor.cs:18
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using UnityEditor.Profiling;
namespace UnityEditor.Experimental
{
public abstract class AssetsModifiedProcessor
{
public HashSet<string> assetsReportedChanged { get; set; }
protected void ReportAssetChanged(string assetChanged)
{
if (assetsReportedChanged == null)
throw new InvalidOperationException("Cannot call ReportAssetChanged outside of the OnAssetsModified callback");
assetsReportedChanged.Add(assetChanged);
}
//Note: changedAssets including added and moved assets may be a usability issue. Review before making public.
///<summary>Fired when the [[AssetDatabase]] detects Asset changes before any Assets are imported.</summary>
///<param name="changedAssets">Paths to the Assets whose file contents have changed. Includes all added and moved Assets.</param>
///<param name="addedAssets">Paths to added Assets.</param>
///<param name="deletedAssets">Paths to deleted Assets.</param>
///<param name="movedAssets">Array of AssetMoveInfo that contains the previous and current location of any moved Assets.</param>
///<description> An Asset will only be reported moved if its .meta file is moved as well.</description>
protected abstract void OnAssetsModified(string[] changedAssets, string[] addedAssets, string[] deletedAssets, AssetMoveInfo[] movedAssets);
internal void Internal_OnAssetsModified(string[] changedAssets, string[] addedAssets, string[] deletedAssets, AssetMoveInfo[] movedAssets)
{
var type = GetType();
using (new EditorPerformanceMarker($"{type.Name}.{nameof(OnAssetsModified)}", type).Auto())
{View on GitHub (pinned to 225b0fbdb5)
Solutions
- Only call ReportAssetChanged inside your OnAssetsModified override, never from other methods.
- If you need to defer processing, collect paths in OnAssetsModified and process them after via EditorApplication.delayCall — do not call ReportAssetChanged from the deferred callback.
- Check the experimental API's lifecycle: ensure you are subclassing correctly and Unity has initialized assetsReportedChanged before your code runs.
Example fix
// before
protected override void OnAssetsModified(string[] changed, string[] added, string[] deleted, AssetMoveInfo[] moved)
{
// ...
}
void SomeOtherMethod()
{
ReportAssetChanged(myAsset); // throws: not in callback
}
// after
protected override void OnAssetsModified(string[] changed, string[] added, string[] deleted, AssetMoveInfo[] moved)
{
foreach (var a in changed)
ReportAssetChanged(a); // correct: inside callback
}
// never call ReportAssetChanged elsewhere Defensive patterns
Strategy: validation
Validate before calling
// Only valid inside OnAssetsModified override
protected override void OnAssetsModified(string[] changed, string[] added, string[] deleted, AssetMoveInfo[] moved)
{
foreach (var a in changed)
ReportAssetChanged(a);
} Type guard
// Cannot type-guard runtime lifecycle; guard by call-site convention static bool IsInAssetsModifiedCallback(AssetsModifiedProcessor p) => p.assetsReportedChanged != null;
Try / catch
try { ReportAssetChanged(asset); }
catch (InvalidOperationException)
{ Debug.LogWarning("ReportAssetChanged called outside OnAssetsModified — ignored"); } Prevention
- Only call ReportAssetChanged inside OnAssetsModified override
- Never call it from constructors, OnEnable, delayCall, or other callbacks
- If deferring work, collect paths in OnAssetsModified and process elsewhere without calling ReportAssetChanged
When it happens
Trigger: Calling ReportAssetChanged from a constructor, from another virtual method, from a timer/coroutine, or from OnAssetsModified after the base class has already finalized the set. Also triggered if a subclass calls it in an overridden method that runs before Unity initializes the HashSet.
Common situations: Subclassing AssetsModifiedProcessor (an experimental API) to receive pre-import asset change notifications, and incorrectly calling ReportAssetChanged outside the OnAssetsModified override — e.g., from OnEnable, from a deferred delegate, or from a separate thread.
Related errors
- Cannot save to clip as there is nothing to save. The method
- Cannot add dependency on invalid path.
- Cannot add source dependency on empty GUID.
- Cannot add dependency on invalid ArtifactKey.
- Cannot add artifact dependency on empty GUID.
AI-assisted analysis of Unity-Technologies/UnityCsReference@225b0fbdb5 (2026-08-13).
Data as JSON: /api/errors/78a4fb897c6a6d4e.
Report an issue: GitHub.