Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/LWJGL/lwjgl3/llms.txt

Use this file to discover all available pages before exploring further.

Most LWJGL 3 problems fall into a small number of categories: native libraries not being found, incorrect context setup, off-heap memory mistakes, or platform-specific requirements. Work through the relevant section below. If none of these resolve your issue, attach a minimal reproducible example when asking for help on the LWJGL forum or Discord.

Debugging tools

Before diving into specific errors, enable LWJGL’s built-in diagnostics. Set these system properties (or call the Configuration API) before any other LWJGL class is loaded:
PropertyTypeDescription
org.lwjgl.util.DebugStaticEnables debug mode. Prints info messages and activates extra runtime checks.
org.lwjgl.util.DebugAllocatorStaticTracks every memAlloc/memFree call and reports all leaks on JVM exit.
org.lwjgl.util.DebugStackStaticDetects asymmetric MemoryStack push/pop calls and reports them immediately.
// Must be called before any other LWJGL class is touched
Configuration.DEBUG.set(true);
Configuration.DEBUG_MEMORY_ALLOCATOR.set(true);
Configuration.DEBUG_STACK.set(true);
See the Memory FAQ for a deeper treatment of LWJGL’s memory model.

LWJGLX debug agent

LWJGLX/debug is a Java agent that automatically detects many common mistakes and can generate a trace log useful when filing bug reports. Add it to your JVM launch arguments:
java -javaagent:lwjglx-debug.jar -cp ... com.example.Main

Common issues

LWJGL cannot locate the native shared library for your platform. There are three ways to fix this:Option 1 — Add the natives JAR to the classpath (recommended)Make sure the natives artifact for your platform is on the classpath. The Maven classifier follows the pattern natives-<platform>, for example:
  • lwjgl-natives-linux.jar (x64)
  • lwjgl-natives-windows.jar (x64)
  • lwjgl-natives-macos.jar (x64)
  • lwjgl-natives-macos-arm64.jar (Apple Silicon)
The easiest way to get the correct artifacts is the LWJGL build configurator, which generates ready-to-use Maven and Gradle declarations.Option 2 — Set java.library.path manuallyExtract the natives to a directory and pass it on the command line:
java -Djava.library.path=/path/to/natives -cp ... com.example.Main
Option 3 — Set the path programmatically
Configuration.LIBRARY_PATH.set("/path/to/natives");
Configuration.LIBRARY_PATH takes priority over java.library.path. Set it before any LWJGL class is loaded.
Two common causes:
  1. Missing GLFW natives — The lwjgl-glfw-natives-<platform> JAR is not on the classpath. Ensure you have the correct platform natives artifact alongside the base lwjgl-glfw.jar.
  2. No error callback — Without an error callback, GLFW silently discards errors. Register one before calling glfwInit() so failures are visible:
GLFWErrorCallback.createPrint(System.err).set();
if (!glfwInit()) {
    throw new IllegalStateException("Unable to initialize GLFW");
}
Always call glfwSetErrorCallback before glfwInit. A missing or misconfigured GLFW_CONTEXT_VERSION_MAJOR / GLFW_CONTEXT_VERSION_MINOR window hint is another frequent reason glfwInit succeeds but glfwCreateWindow returns NULL.
LWJGL’s explicit memory management API (MemoryUtil.memAlloc, memAllocInt, etc.) allocates off-heap memory that the garbage collector does not manage. Every allocation must be paired with a memFree call.
ByteBuffer buf = memAlloc(1024);
try {
    // use buf
} finally {
    memFree(buf); // always free
}
For short-lived allocations, prefer MemoryStack — it is automatically reclaimed when the stack frame is popped:
try (MemoryStack stack = stackPush()) {
    IntBuffer width  = stack.mallocInt(1);
    IntBuffer height = stack.mallocInt(1);
    glfwGetWindowSize(window, width, height);
}
Enable DEBUG_MEMORY_ALLOCATOR to get a full allocation report (with stack traces) on JVM exit:
Configuration.DEBUG_MEMORY_ALLOCATOR.set(true);
See the Memory FAQ for a complete guide to LWJGL memory management.
OpenGL contexts are thread-local. The context must be made current on the thread that issues rendering commands, and only one thread may hold the context at a time.
// On your render thread:
glfwMakeContextCurrent(window);
GL.createCapabilities();

// Render loop
while (!glfwWindowShouldClose(window)) {
    glClear(GL_COLOR_BUFFER_BIT);
    // draw calls here
    glfwSwapBuffers(window);
    glfwPollEvents();
}
If you transfer the context between threads, call glfwMakeContextCurrent(0) on the releasing thread before calling glfwMakeContextCurrent(window) on the acquiring thread.
The two most frequent causes are:
  1. Forgot GL.createCapabilities() — This call initializes the OpenGL function pointers for the current context. Without it every GL* call is a no-op (or worse, a crash).
glfwMakeContextCurrent(window);
GL.createCapabilities(); // required after making context current
  1. No swap / poll loop — Make sure your render loop calls both glfwSwapBuffers(window) and glfwPollEvents(). If glfwPollEvents is never called the window will not process its event queue and may appear frozen or black.
Optionally enable V-sync to avoid tearing:
glfwSwapInterval(1); // 1 = vsync on
MemoryStack uses a push/pop discipline. Each stackPush() call must be balanced by a corresponding stackPop() (or use try-with-resources). If a method pushes a frame and returns without popping it, DEBUG_STACK reports the violation immediately with the offending stack trace.Always use the try-with-resources pattern:
try (MemoryStack stack = stackPush()) {
    // allocations here are automatically freed on block exit
}
Enable the check:
Configuration.DEBUG_STACK.set(true);
On macOS, GLFW (and other Cocoa-based windowing code) must run on the first thread — the thread that is started by the OS when the process launches. If you launch from a standard main method the JVM may start it on a non-first thread, causing a crash or hang.Add the -XstartOnFirstThread JVM flag to your launch configuration:
java -XstartOnFirstThread -cp ... com.example.Main
In Gradle, add this to your run task:
if (System.getProperty("os.name").contains("Mac")) {
    jvmArgs("-XstartOnFirstThread")
}
Forgetting -XstartOnFirstThread on macOS is one of the most common causes of immediate crashes or a black non-responsive window.

Still stuck?

Build docs developers (and LLMs) love