Quick answer: Don’t call AssetDatabase.Refresh inside OnPreprocessAsset. Defer with EditorApplication.delayCall += () => Refresh().

Custom importer logs “Asset import has been called recursively” or hangs. You called Refresh from inside an import callback.

The Fix

public class MyImporter : AssetPostprocessor {
    void OnPreprocessAsset() {
        // safe: import-time mutations
        if (assetImporter is TextureImporter ti) ti.maxTextureSize = 2048;

        // unsafe: Refresh, SaveAssets, ReimportAll
    }

    static void OnPostprocessAllAssets(string[] imported, ...) {
        EditorApplication.delayCall += () => {
            AssetDatabase.Refresh();   // after import settles
        };
    }
}

delayCall fires on next editor frame after the import batch completes — safe scope to perform Refresh.

Verifying

Editor doesn’t hang. Console clean. Assets reimport on next iteration if Refresh detected new files.

“Don’t Refresh in import. Defer to delayCall.”

Related Issues

For Asset Preset, see presets. For Pipeline V2 cache, see cache.

delayCall after. No deadlock.