EditorGUILayout.ColorField inside GUILayout.Window causes ExitGUIException

I’m coding an EditorWindow. My problem is, when I use a EditorGUILayout.ColorField inside a GUILayout.Window, an ExitGUIException occurs when I click onto the color field.

I was able to boil down the problem to the following script:

using UnityEngine;
using UnityEditor;

public class TestEditor : EditorWindow 
{
	Color mColor = Color.green;
	
	[MenuItem ("Test/TestEditor")]
	static void Init() 
	{
		EditorWindow.GetWindow( typeof( TestEditor ) );		
	}
	
	public void OnGUI () 
	{
		BeginWindows();
		GUILayout.Window ( 7, new Rect( 100, 100, 300, 50), DoWindow, "Window" );
 		EndWindows();
	}

	void DoWindow( int id )
	{
		mColor = EditorGUILayout.ColorField("Click here -->", mColor);
	}
}

When I open the test editor and click on the color field, the following error occurs:

ExitGUIException: Exception of type ‘UnityEngine.ExitGUIException’ was thrown.
UnityEngine.GUIUtility.ExitGUI ()
(at C:/BuildAgent/work/812c4f5049264fad/Runtime/ExportGenerated/Editor/GUIUtility.cs:95)
UnityEditor.EditorGUI.DoColorField (Rect position, Int32 id, Color value, Boolean showEyedropper, Boolean showAlpha)
(at C:/BuildAgent/work/812c4f5049264fad/Editor/MonoGenerated/Editor/EditorGUI.cs:2222)
UnityEditor.EditorGUI.ColorField (Rect position, UnityEngine.GUIContent label, Color value)
(at C:/BuildAgent/work/812c4f5049264fad/Editor/MonoGenerated/Editor/EditorGUI.cs:2201)
UnityEditor.EditorGUI.ColorField (Rect position, System.String label, Color value)
(at C:/BuildAgent/work/812c4f5049264fad/Editor/MonoGenerated/Editor/EditorGUI.cs:2198)
UnityEditor.EditorGUILayout.ColorField (System.String label, Color value, UnityEngine.GUILayoutOption options)
(at C:/BuildAgent/work/812c4f5049264fad/Editor/MonoGenerated/Editor/EditorGUI.cs:3834)
TestEditor.DoWindow (Int32 id)
(at Assets/Editor/TestEditor.cs:23)
UnityEngine.GUILayout+LayoutedWindow.DoWindow (Int32 windowID)
(at C:/BuildAgent/work/812c4f5049264fad/Runtime/ExportGenerated/Editor/GUILayout.cs:317)
UnityEngine.GUI.CallWindowDelegate (UnityEngine.WindowFunction func, Int32 id, UnityEngine.GUISkin _skin, Int32 forceRect, Single width, Single height, UnityEngine.GUIStyle style)
(at C:/BuildAgent/work/812c4f5049264fad/Runtime/ExportGenerated/Editor/GUI.cs:1023)
UnityEditor.EditorWindow:BeginWindows()
TestEditor:OnGUI()
(at Assets/Editor/TestEditor.cs:16)
UnityEditor.DockArea:OnGUI()

The error does not occur if I write the EditorGUILayout.ColorField simply into OnGUI instead of a window. Also, other fields like EditorGUILayout.CurveField or EditorGUILayout.FloatField don’t cause any problems.

What am I doing wrong?

The problem:
ExitGUIException thrown when clicking the color picker on a color field or the “Select” area on any object field.

Edit: Updated based on important information from @Bunny83’s comments.

Unity throws this ExitGUIException EVERY time one of these controls is clicked. This exception is used by Unity and needs to be propagated. If you have wrapped the control in a try/catch statement (including upstream), it will catch the exception. If you don’t have any try/catch statements, you’ll never even see this exception thrown.

Any Try/Catch blocks around or upstream of a ColorField or ObjectField must propagate the ExitGUIException to allow Unity to receive it.

try {
    // code
} catch (UnityEngine.ExitGUIException) {
    throw;
} catch (System.Exception e) {
    // Handle the exception
}

I unexpectedly ran into this error the other day, and found this post. But since the issue had NOTHING to do with try/catch blocks, I figured I’d share what it was, though probably a rare issue.

My problem seemed to have something to do with HOW my editor GUI function was invoked. In particular, I was using reflection to find a particular function that I wanted. The result was stored in a System.Reflection.MethodInfo variable.

If I called Invoke() on that variable, the exception was generated.

method.Invoke(null, null);

However, if I converted the MethodInfo into a Delegate, and then called the delegate, the exception vanished.

public delegate void PreferencesGUI();
PreferencesGUI func = (PreferencesGUI)Delegate.CreateDelegate(typeof(PreferencesGUI), method);
func();

Honestly, I’m not quite sure what the internal difference between these two invocation methods is, nor why it should generate an exception. Update: answered in bunny’s comment below

@Bunny83 : thought you might find this interesting/good to know. Update: thank you!

I am having this same problem. However the curve is giving me issues as well. Have you found the answer?

I am having the same problem in custom inspector,when I use a reflector method to invoke a generic method to create a object type filed(e.g. colorField,CurveField,ObjectFiled…).

private static Color DoColorField(Rect position, int id, Color value, bool showEyedropper, bool showAlpha)
{
    Event current = Event.current;
    GUIStyle colorField = EditorStyles.colorField;
    Color color = value;
    value = !showMixedValue ? value : Color.white;
    EventType typeForControl = current.GetTypeForControl(id);
    switch (typeForControl)
    {
        case EventType.KeyDown:
            if ((GUIUtility.keyboardControl == id) && (((current.keyCode == KeyCode.Space) || (current.keyCode == KeyCode.Return)) || (current.keyCode == KeyCode.KeypadEnter)))
            {
                Event.current.Use();
                showMixedValue = false;
                ColorPicker.Show(GUIView.current, value, showAlpha);
                GUIUtility.ExitGUI();
            }
            return color;

        case EventType.Repaint:
            Rect rect;
            if (!showEyedropper)
            {
                rect = position;
            }
            else
            {
                rect = colorField.padding.Remove(position);
            }
            if (showEyedropper && (s_ColorPickID == id))
            {
                Color pickedColor = EyeDropper.GetPickedColor();
                pickedColor.a = value.a;
                EditorGUIUtility.DrawColorSwatch(rect, pickedColor, showAlpha);
            }
            else
            {
                EditorGUIUtility.DrawColorSwatch(rect, value, showAlpha);
            }
            if (showEyedropper)
            {
                colorField.Draw(position, GUIContent.none, id);
                return color;
            }
            EditorStyles.colorPickerBox.Draw(position, GUIContent.none, id);
            return color;
    }
    if (typeForControl != EventType.MouseDown)
    {
        if ((typeForControl == EventType.ExecuteCommand) && (GUIUtility.keyboardControl == id))
        {
            int num;
            string commandName = current.commandName;
            if (commandName == null)
            {
                return color;
            }
            if (<>f__switch$map10 == null)
            {
                Dictionary<string, int> dictionary = new Dictionary<string, int>(4);
                dictionary.Add("EyeDropperUpdate", 0);
                dictionary.Add("EyeDropperClicked", 1);
                dictionary.Add("EyeDropperCancelled", 2);
                dictionary.Add("ColorPickerChanged", 3);
                <>f__switch$map10 = dictionary;
            }
            if (!<>f__switch$map10.TryGetValue(commandName, out num))
            {
                return color;
            }
            switch (num)
            {
                case 0:
                    HandleUtility.Repaint();
                    return color;

                case 1:
                {
                    GUI.changed = true;
                    HandleUtility.Repaint();
                    Color lastPickedColor = EyeDropper.GetLastPickedColor();
                    lastPickedColor.a = value.a;
                    s_ColorPickID = 0;
                    return lastPickedColor;
                }
                case 2:
                    HandleUtility.Repaint();
                    s_ColorPickID = 0;
                    return color;

                case 3:
                    GUI.changed = true;
                    HandleUtility.Repaint();
                    return ColorPicker.color;
            }
        }
        return color;
    }
    if (showEyedropper)
    {
        position.width -= 20f;
    }
    if (position.Contains(current.mousePosition))
    {
        GUIUtility.keyboardControl = id;
        showMixedValue = false;
        ColorPicker.Show(GUIView.current, value, showAlpha);
        GUIUtility.ExitGUI();
    }
    if (showEyedropper)
    {
        position.width += 20f;
        if (position.Contains(current.mousePosition))
        {
            GUIUtility.keyboardControl = id;
            EyeDropper.Start(GUIView.current);
            s_ColorPickID = id;
            GUIUtility.ExitGUI();
        }
    }
    return color;
}

The source code of DoColorField
There are there calls of “GUIUtility.ExitGUI()” can cause the exception
I can’t what can cause this.

This is still bugging me. If you can, change the catch block so you just catch UnityException’s:

try
{
}
catch (Exception e)
{
	Debug.LogException(e);
}

// Instead of

try
{
}
catch (UnityException e)
{
	Debug.LogException(e);
}

I needed to handle the exceptions, so my solution was:

try
{
        //Your Code
}
catch (Exception e)
{
    if(e.GetType() != typeof(ExitGUIException))
        {
            //The exception is real, handle here
            Debug.LogException(e);
        }
    }
}

I just did a deep dive into investigating the ExitGUIException and thought I’d share my findings here.

Unity’s GUIUtility has a method called ExitGUI that looks like this:

public static void ExitGUI()
{
	GUIUtility.guiIsExiting = true;
	throw new ExitGUIException();
}

Unity’s documentation on GUIUtility.ExitGUI states the following:

Use GUIUtility.ExitGUI in situations -

  • such as when a change in some value might change what controls are
    displayed next. Using this method can
    prevent errors such as
    ArgumentException: Getting control 0’s
    position in a group with only 0
    controls when doing Repaint.

So ExitGUIException is something that Unity intentionally throws in its internal code whenever it wants to exit out of GUI drawing code in the middle of it.

This usually happens when the user interacts with the GUI in some way, like clicks a button.

Unity will internally handle suppressing this exception silently without any errors appearing in the user’s console, as long as you don’t catch it yourself.

Here is how Unity’s Inspector Window handles ExitGUIException:

try
{
	editor.OnInspectorGUI();
}
catch (Exception exception)
{
	if (GUIUtility.ShouldRethrowException(exception))
	{
		throw;
	}
	Debug.LogException(exception);
}

And here is what GUIUtility.ShouldRethrowException looks like:

internal static bool ShouldRethrowException(Exception exception)
{
	return GUIUtility.IsExitGUIException(exception);
}

internal static bool IsExitGUIException(Exception exception)
{
	while (exception is TargetInvocationException && exception.InnerException != null)
	{
		exception = exception.InnerException;
	}
	return exception is ExitGUIException;
}

You should do the same thing in your code. Whenever you have OnGUI logic inside a try-catch statement, you should check if the caught exception is an ExitGUIException, and if it is, rethrow it.

Unfortunately for some reason Unity has decided to make GUIUtility.ShouldRethrowException into an internal method, so you will have to replicate the method in your own code before you can use it.

You can do so by adding this code into your project inside a file called “ExitGUIUtility.cs”:

using System;
using System.Reflection;
using UnityEngine;

public static class ExitGUIUtility
{
	public static bool ShouldRethrowException(Exception exception)
	{
		return IsExitGUIException(exception);
	}

	public static bool IsExitGUIException(Exception exception)
	{
		while (exception is TargetInvocationException && exception.InnerException != null)
		{
			exception = exception.InnerException;
		}
		return exception is ExitGUIException;
	}
}

You can then use it like this:

try
{
	mColor = EditorGUILayout.ColorField("Click here -->", mColor);
}
catch(Exception e)
{
	if(ExitGUIUtility.ShouldRethrowException(e))
	{
		throw;
	}
	Debug.LogException(e);
}