google/ExoPlayer · error · MediaDrmCallbackException
No license URL
Error message
No license URL
What it means
HttpMediaDrmCallback.executeKeyRequest() must resolve a license server URL for every key/license request. It first uses the URL embedded in the KeyRequest (which comes from the DRM init data / MediaDrm), and if forceDefaultLicenseUrl is set or that URL is empty it falls back to defaultLicenseUrl (the constructor argument). If both are empty it throws MediaDrmCallbackException wrapping IllegalStateException("No license URL"), aborting key acquisition.
Source
Thrown at library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java:145
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request)
throws MediaDrmCallbackException {
String url =
request.getDefaultUrl() + "&signedRequest=" + Util.fromUtf8Bytes(request.getData());
return executePost(
dataSourceFactory,
url,
/* httpBody= */ null,
/* requestProperties= */ Collections.emptyMap());
}
@Override
public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws MediaDrmCallbackException {
String url = request.getLicenseServerUrl();
if (forceDefaultLicenseUrl || TextUtils.isEmpty(url)) {
url = defaultLicenseUrl;
}
if (TextUtils.isEmpty(url)) {
throw new MediaDrmCallbackException(
new DataSpec.Builder().setUri(Uri.EMPTY).build(),
Uri.EMPTY,
/* responseHeaders= */ ImmutableMap.of(),
/* bytesLoaded= */ 0,
/* cause= */ new IllegalStateException("No license URL"));
}
Map<String, String> requestProperties = new HashMap<>();
// Add standard request properties for supported schemes.
String contentType =
C.PLAYREADY_UUID.equals(uuid)
? "text/xml"
: (C.CLEARKEY_UUID.equals(uuid) ? "application/json" : "application/octet-stream");
requestProperties.put("Content-Type", contentType);
if (C.PLAYREADY_UUID.equals(uuid)) {
requestProperties.put(
"SOAPAction", "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense");
}
// Add additional request properties.View on GitHub (pinned to dd430f7053)
Solutions
- Pass an explicit default license URL when constructing the callback: new HttpMediaDrmCallback("https://license.example.com/path", dataSourceFactory) — this covers content lacking an embedded URL.
- If URLs are embedded per-stream, keep them but do NOT set forceDefaultLicenseUrl(true) with an empty default; either supply both or rely on the embedded URL.
- Fix the packaging side: include the license URL in the manifest (DASH ContentProtection LaURL / PlayReady PRO) or in the ClearKey JSON so request.getLicenseServerUrl() is non-empty.
- Log which path produced the empty URL (embedded vs default) to confirm whether content or app config is at fault.
Example fix
// before
DrmSessionManager drm = new DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
.build(new HttpMediaDrmCallback("", httpDataSourceFactory)); // empty default
// after
DrmSessionManager drm = new DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
.build(new HttpMediaDrmCallback("https://license.example.com/widevine", httpDataSourceFactory)); Defensive patterns
Strategy: validation
Validate before calling
String licenseUrl = "https://license.example.com/widevine"; // app config
HttpMediaDrmCallback callback = new HttpMediaDrmCallback(licenseUrl, httpDataSourceFactory);
// Guard: never construct with an empty/blank default when init data may lack a URL
if (licenseUrl == null || licenseUrl.trim().isEmpty()) {
throw new IllegalStateException("HttpMediaDrmCallback requires a license URL for this content");
} Try / catch
@Override
public void onPlayerError(PlaybackException error) {
if (error.getCause() instanceof DrmSessionException
&& error.getCause().getCause() instanceof MediaDrmCallbackException) {
MediaDrmCallbackException drmEx = (MediaDrmCallbackException) error.getCause().getCause();
if (drmEx.getCause() instanceof IllegalStateException
&& "No license URL".equals(drmEx.getCause().getMessage())) {
// surface 'DRM not configured' UI instead of a generic playback failure
}
}
} Prevention
- Centralize the license server URL in one config constant used by every HttpMediaDrmCallback construction.
- When forceDefaultLicenseUrl(true) is set, always supply a non-empty default — forcing an empty default guarantees this throw.
- Verify stream packaging: DASH ContentProtection should carry LaURL (PlayReady) or you must ship the Widevine URL out-of-band.
- Smoke-test one protected stream per environment in CI to catch missing-URL regressions early.
When it happens
Trigger: Calling new HttpMediaDrmCallback(null /* or "" */, dataSourceFactory) and playing content whose DRM init data carries no license URL (e.g. DASH ContentProtection without LaURL, raw PSSH boxes without a URL); setting forceDefaultLicenseUrl=true while defaultLicenseUrl is empty; ClearKey where the JSON/media path omitted the license server; PlayReady where LaURL was stripped from the manifest.
Common situations: License server passed only in the manifest on some streams but not others; test streams with bare PSSH init data; typos like passing a placeholder empty string; migrating from LocalMediaDrmCallback to HttpMediaDrmCallback and forgetting the URL argument; CDN/manifest re-packagers that drop LaURL from ContentProtection elements.
Related errors
- Attempting to open a session using a dummy ExoMediaDrm.
- ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED
- ERROR_CODE_IO_BAD_HTTP_STATUS
- ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE
- No mixing matrix for input channel count {audioFormat}
AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14).
Data as JSON: /api/errors/f8a9f200de5d4be4.
Report an issue: GitHub.