{"record":{"id":"9502932fc0564108","repo":"google/ExoPlayer","slug":"not-in-applications-main-thread","errorCode":null,"errorMessage":"Not in applications main thread","messagePattern":"Not in applications main thread","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"library/common/src/main/java/com/google/android/exoplayer2/util/Assertions.java","lineNumber":232,"sourceCode":"  @EnsuresNonNull({\"#1\"})\n  @Pure\n  public static String checkNotEmpty(@Nullable String string, Object errorMessage) {\n    if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && TextUtils.isEmpty(string)) {\n      throw new IllegalArgumentException(String.valueOf(errorMessage));\n    }\n    return string;\n  }\n\n  /**\n   * Throws {@link IllegalStateException} if the calling thread is not the application's main\n   * thread.\n   *\n   * @throws IllegalStateException If the calling thread is not the application's main thread.\n   */\n  @Pure\n  public static void checkMainThread() {\n    if (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED && Looper.myLooper() != Looper.getMainLooper()) {\n      throw new IllegalStateException(\"Not in applications main thread\");\n    }\n  }\n}\n","sourceCodeStart":214,"sourceCodeEnd":236,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/common/src/main/java/com/google/android/exoplayer2/util/Assertions.java#L214-L236","documentation":"Thrown by Assertions.checkMainThread() when the calling thread's Looper does not match the application's main Looper. ExoPlayer requires most Player method calls (play, pause, seek, release, etc.) to happen on the application thread that created the player, because the player is not thread-safe and its state must be accessed single-threaded. The check only fires when assertions are enabled (ExoPlayerLibraryInfo.ASSERTIONS_ENABLED), so the same bug may silently corrupt state in release builds.","triggerScenarios":"Calling any player-facing API annotated to require the main thread from a background/loader/network thread: e.g. calling player.play() from a Runnable on a HandlerThread, from inside runOnUiThread-less callbacks, from a coroutine on Dispatchers.IO/Default, or from ExoPlayer's own analytics/playlist callbacks which are invoked on the playback (app) thread but where the app then hops threads.","commonSituations":"Doing player control inside.observeForever on a background-lived LiveData, calling player methods from a Thread used for download/DRM work, using coroutines without withContext(ContextCompat.getMainExecutor(...)), or constructing the player with a custom playbackLooper and assuming calls from any thread are safe.","solutions":["Move the player interaction onto the main thread: wrap the call in activity.runOnUiThread { ... }, view.post { ... }, or mainHandler.post { ... }","In coroutines, wrap calls in withContext(Dispatchers.Main) { player.play() }","If you intentionally run on a dedicated thread, create the player on that same thread/Looper so its invariant thread matches, and keep all calls there","Audit every player method invocation reachable from callbacks (Player.Listener, DRM callbacks, ImaAdsLoader) and confirm the thread they arrive on before touching the player"],"exampleFix":"// before\nthread {\n    player.seekTo(positionMs) // throws: background thread\n}\n// after\nthread {\n    mainHandler.post {\n        player.seekTo(positionMs)\n    }\n}","handlingStrategy":"validation","validationCode":"if (Looper.myLooper() != Looper.getMainLooper()) {\n  throw new IllegalStateException(\"Call player APIs on the main thread\");\n}","typeGuard":null,"tryCatchPattern":"Do not catch: this exception marks a threading bug; catching hides state corruption. Fix the call site thread.","preventionTips":["Centralize all player calls in a single UI-layer wrapper that asserts the main thread","In coroutines, make PlayerController a main-dispatcher object; annotate functions with @MainThread","Never call the player from DataSource, Extractor, or codec callbacks — post to the main handler instead"],"tags":["threading","android","player","assertion"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}