{"record":{"id":"667c14c2e56cc86c","repo":"google/ExoPlayer","slug":"error-instantiating-opus-extension","errorCode":null,"errorMessage":"Error instantiating Opus extension","messagePattern":"Error instantiating Opus extension","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java","lineNumber":507,"sourceCode":"    }\n\n    try {\n      // Full class names used for constructor args so the LINT rule triggers if any of them move.\n      Class<?> clazz = Class.forName(\"com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer\");\n      Constructor<?> constructor =\n          clazz.getConstructor(\n              android.os.Handler.class,\n              com.google.android.exoplayer2.audio.AudioRendererEventListener.class,\n              com.google.android.exoplayer2.audio.AudioSink.class);\n      Renderer renderer =\n          (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);\n      out.add(extensionRendererIndex++, renderer);\n      Log.i(TAG, \"Loaded LibopusAudioRenderer.\");\n    } catch (ClassNotFoundException e) {\n      // Expected if the app was built without the extension.\n    } catch (Exception e) {\n      // The extension is present, but instantiation failed.\n      throw new RuntimeException(\"Error instantiating Opus extension\", e);\n    }\n\n    try {\n      // Full class names used for constructor args so the LINT rule triggers if any of them move.\n      Class<?> clazz = Class.forName(\"com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer\");\n      Constructor<?> constructor =\n          clazz.getConstructor(\n              android.os.Handler.class,\n              com.google.android.exoplayer2.audio.AudioRendererEventListener.class,\n              com.google.android.exoplayer2.audio.AudioSink.class);\n      Renderer renderer =\n          (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);\n      out.add(extensionRendererIndex++, renderer);\n      Log.i(TAG, \"Loaded LibflacAudioRenderer.\");\n    } catch (ClassNotFoundException e) {\n      // Expected if the app was built without the extension.\n    } catch (Exception e) {\n      // The extension is present, but instantiation failed.","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java#L489-L525","documentation":"DefaultRenderersFactory reflectively instantiates the optional Opus decoder renderer (com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer) using its (Handler, AudioRendererEventListener, AudioSink) constructor. ClassNotFoundException means the extension was not packaged and is fine; any other reflective failure means the extension is present but broken, so a RuntimeException wrapping the cause is thrown to abort player construction rather than proceed with a half-working extension.","triggerScenarios":"Extension renderer mode ON/PREFER with the exoplayer-opus artifact on the classpath whose constructor signature does not match, is obfuscated away, or throws during instantiation (native libopus .so load failure).","commonSituations":"Version drift between exoplayer-core and exoplayer-opus; release builds with aggressive shrinking that renames Handler/AudioSink parameter types referenced by string in getConstructor; missing libopus .so for armeabi-v7a/arm64-v8a/x86 splits.","solutions":["Unwrap RuntimeException.getCause() to distinguish NoSuchMethodException (signature drift) from UnsatisfiedLinkError (missing native lib).","Align exoplayer-opus and exoplayer-core to the exact same version.","Add keep rules for com.google.android.exoplayer2.ext.opus.** including constructors.","Package libopus for all supported ABIs or switch to EXTENSION_RENDERER_MODE_OFF and rely on MediaCodec Opus decoders (Android 10+)."],"exampleFix":"// before\nnew ExoPlayer.Builder(\n    new DefaultRenderersFactory(ctx)\n        .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER))\n    .build(ctx);\n\n// after — construct the Opus renderer directly from the extension artifact\n// (no reflection, fails at compile time if APIs drift)\nRenderersFactory factory =\n    new DefaultRenderersFactory(ctx) {\n      @Override protected void buildAudioRenderers(...) {\n        out.add(index++, new LibopusAudioRenderer(eventHandler, eventListener, audioSink));\n        super.buildAudioRenderers(...);\n      }\n    };","handlingStrategy":"try-catch","validationCode":"boolean opusExtensionOk = false;\ntry {\n  Class.forName(\"com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer\")\n      .getConstructor(Handler.class, AudioRendererEventListener.class, AudioSink.class);\n  opusExtensionOk = true;\n} catch (ReflectiveOperationException ignored) {\n}\n// prefer platform Opus (Android 10+) unless software decode needed","typeGuard":null,"tryCatchPattern":"try {\n  return new ExoPlayer.Builder(opusFactory).build(context);\n} catch (RuntimeException e) {\n  logExtensionFailure(\"opus\", e.getCause());\n  return new ExoPlayer.Builder(new DefaultRenderersFactory(context)).build(context);\n}","preventionTips":["Same version for core and exoplayer-opus","R8 keep rules for ext.opus package","Bundle libopus for every supported ABI or skip the extension","Consider platform Opus support on Android 10+ before shipping the extension"],"tags":["exoplayer","reflection","extension","opus","audio","initialization"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}