{"record":{"id":"f8a9f200de5d4be4","repo":"google/ExoPlayer","slug":"no-license-url","errorCode":null,"errorMessage":"No license URL","messagePattern":"No license URL","errorType":"exception","errorClass":"MediaDrmCallbackException","httpStatus":null,"severity":"error","filePath":"library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java","lineNumber":145,"sourceCode":"  public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request)\n      throws MediaDrmCallbackException {\n    String url =\n        request.getDefaultUrl() + \"&signedRequest=\" + Util.fromUtf8Bytes(request.getData());\n    return executePost(\n        dataSourceFactory,\n        url,\n        /* httpBody= */ null,\n        /* requestProperties= */ Collections.emptyMap());\n  }\n\n  @Override\n  public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws MediaDrmCallbackException {\n    String url = request.getLicenseServerUrl();\n    if (forceDefaultLicenseUrl || TextUtils.isEmpty(url)) {\n      url = defaultLicenseUrl;\n    }\n    if (TextUtils.isEmpty(url)) {\n      throw new MediaDrmCallbackException(\n          new DataSpec.Builder().setUri(Uri.EMPTY).build(),\n          Uri.EMPTY,\n          /* responseHeaders= */ ImmutableMap.of(),\n          /* bytesLoaded= */ 0,\n          /* cause= */ new IllegalStateException(\"No license URL\"));\n    }\n    Map<String, String> requestProperties = new HashMap<>();\n    // Add standard request properties for supported schemes.\n    String contentType =\n        C.PLAYREADY_UUID.equals(uuid)\n            ? \"text/xml\"\n            : (C.CLEARKEY_UUID.equals(uuid) ? \"application/json\" : \"application/octet-stream\");\n    requestProperties.put(\"Content-Type\", contentType);\n    if (C.PLAYREADY_UUID.equals(uuid)) {\n      requestProperties.put(\n          \"SOAPAction\", \"http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense\");\n    }\n    // Add additional request properties.","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java#L127-L163","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nDrmSessionManager drm = new DefaultDrmSessionManager.Builder()\n    .setUuidAndExoMediaDrmProvider(C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)\n    .build(new HttpMediaDrmCallback(\"\", httpDataSourceFactory)); // empty default\n\n// after\nDrmSessionManager drm = new DefaultDrmSessionManager.Builder()\n    .setUuidAndExoMediaDrmProvider(C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)\n    .build(new HttpMediaDrmCallback(\"https://license.example.com/widevine\", httpDataSourceFactory));","handlingStrategy":"validation","validationCode":"String licenseUrl = \"https://license.example.com/widevine\"; // app config\nHttpMediaDrmCallback callback = new HttpMediaDrmCallback(licenseUrl, httpDataSourceFactory);\n// Guard: never construct with an empty/blank default when init data may lack a URL\nif (licenseUrl == null || licenseUrl.trim().isEmpty()) {\n  throw new IllegalStateException(\"HttpMediaDrmCallback requires a license URL for this content\");\n}","typeGuard":null,"tryCatchPattern":"@Override\npublic void onPlayerError(PlaybackException error) {\n  if (error.getCause() instanceof DrmSessionException\n      && error.getCause().getCause() instanceof MediaDrmCallbackException) {\n    MediaDrmCallbackException drmEx = (MediaDrmCallbackException) error.getCause().getCause();\n    if (drmEx.getCause() instanceof IllegalStateException\n        && \"No license URL\".equals(drmEx.getCause().getMessage())) {\n      // surface 'DRM not configured' UI instead of a generic playback failure\n    }\n  }\n}","preventionTips":["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."],"tags":["drm","exoplayer","android","license-server","configuration","network"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}