OpenRA/OpenRA · critical · InvalidOperationException

Can not bind OpenGL context. (Error: {SDL.SDL_GetError()})

Error message

Can not bind OpenGL context. (Error: {SDL.SDL_GetError()})

What it means

Thrown in InitializeOpenGL when, after pinning the render thread affinity, SDL_GL_MakeCurrent(window, context) returns < 0. The context was successfully created and released on the main thread in the constructor, but rebinding it to the render thread fails. SDL_GetError() is interpolated to surface the driver reason, which is usually that the context is still current elsewhere, the thread is not allowed to bind it, or the window/context were invalidated.

Source

Thrown at OpenRA.Platforms.Default/Sdl2GraphicsContext.cs:41

		public string GLVersion => OpenGL.Version;

		public Sdl2GraphicsContext(Sdl2PlatformWindow window)
		{
			this.window = window;

			// SDL requires us to create the GL context on the main thread to avoid various platform-specific issues.
			// We must then release it from the main thread before we rebind it to the render thread (in InitializeOpenGL below).
			context = SDL.SDL_GL_CreateContext(window.Window);
			if (context == IntPtr.Zero || SDL.SDL_GL_MakeCurrent(window.Window, IntPtr.Zero) < 0)
				throw new InvalidOperationException($"Can not create OpenGL context. (Error: {SDL.SDL_GetError()})");
		}

		internal void InitializeOpenGL()
		{
			SetThreadAffinity();

			if (SDL.SDL_GL_MakeCurrent(window.Window, context) < 0)
				throw new InvalidOperationException($"Can not bind OpenGL context. (Error: {SDL.SDL_GetError()})");

			OpenGL.Initialize();
			OpenGL.CheckGLError();

			OpenGL.glGenVertexArrays(1, out var vao);
			OpenGL.CheckGLError();
			OpenGL.glBindVertexArray(vao);
			OpenGL.CheckGLError();
		}

		public IVertexBuffer<T> CreateEmptyVertexBuffer<T>(int size) where T : struct
		{
			VerifyThreadAffinity();
			return new VertexBuffer<T>(size);
		}

		public IVertexBuffer<T> CreateVertexBuffer<T>(T[] data, bool dynamic = true) where T : struct
		{

View on GitHub (pinned to a520984d91)

Solutions

  1. Read SDL_GetError() from the message — 'context already current' vs 'invalid context/window' points to different causes.
  2. Confirm the constructor's SDL_GL_MakeCurrent(window, IntPtr.Zero) release actually succeeded (it is checked together with context creation in 603 — if that did not throw, release worked).
  3. Ensure InitializeOpenGL runs on exactly one dedicated render thread and that no other thread has called SDL_GL_MakeCurrent with this context.
  4. Verify the window handle passed to MakeCurrent is the same live window used to create the context and has not been destroyed/recreated.
  5. Update SDL2 bindings/driver; on macOS confirm the main-thread release and render-thread bind follow Cocoa's thread rules.

Example fix

// before: rebinding a context that was never released on the main thread
internal void InitializeOpenGL()
{
    SetThreadAffinity();
    if (SDL.SDL_GL_MakeCurrent(window.Window, context) < 0)
        throw new InvalidOperationException(...); // -> fires
}

// after: guarantee the constructor released it first
public Sdl2GraphicsContext(Sdl2PlatformWindow window)
{
    context = SDL.SDL_GL_CreateContext(window.Window);
    if (context == IntPtr.Zero || SDL.SDL_GL_MakeCurrent(window.Window, IntPtr.Zero) < 0)
        throw new InvalidOperationException(...);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the context is released on the main thread before binding on the render thread.
if (SDL.SDL_GL_MakeCurrent(window.Window, IntPtr.Zero) < 0)
    throw new InvalidOperationException($"Failed to release context on main thread: {SDL.SDL_GetError()}");

Try / catch

try
{
    graphicsContext.InitializeOpenGL();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("bind OpenGL context"))
{
    var sdlErr = SDL.SDL_GetError();
    if (sdlErr.Contains("already current")) { /* release elsewhere and retry once */ }
    else throw;
}

Prevention

When it happens

Trigger: The context was never released on the main thread (constructor release failed silently upstream), the context is already current on another thread, the window was destroyed/recreated between construction and InitializeOpenGL, or the platform restricts context binding to the thread that created it.

Common situations: Multi-threaded render bootstrap ordering bug; window recreation during display-mode changes; driver/platform (notably some macOS or NVIDIA configurations) that forbid moving a context across threads; SDL/library version regression in SDL_GL_MakeCurrent.

Related errors


AI-assisted analysis of OpenRA/OpenRA@a520984d91 (2026-08-13). Data as JSON: /api/errors/48f16bdc866181cd. Report an issue: GitHub.