{"record":{"id":"d8cfc66860aa5ebb","repo":"google/ExoPlayer","slug":"the-item-must-specify-its-mimetype","errorCode":null,"errorMessage":"The item must specify its mimeType","messagePattern":"The item must specify its mimeType","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/DefaultMediaItemConverter.java","lineNumber":101,"sourceCode":"        metadataBuilder.setComposer(metadata.getString(MediaMetadata.KEY_COMPOSER));\n      }\n      if (metadata.containsKey(MediaMetadata.KEY_DISC_NUMBER)) {\n        metadataBuilder.setDiscNumber(metadata.getInt(MediaMetadata.KEY_DISC_NUMBER));\n      }\n      if (metadata.containsKey(MediaMetadata.KEY_TRACK_NUMBER)) {\n        metadataBuilder.setTrackNumber(metadata.getInt(MediaMetadata.KEY_TRACK_NUMBER));\n      }\n    }\n    // `mediaQueueItem` came from `toMediaQueueItem()` so the custom JSON data must be set.\n    return getMediaItem(\n        Assertions.checkNotNull(mediaInfo.getCustomData()), metadataBuilder.build());\n  }\n\n  @Override\n  public MediaQueueItem toMediaQueueItem(MediaItem mediaItem) {\n    Assertions.checkNotNull(mediaItem.localConfiguration);\n    if (mediaItem.localConfiguration.mimeType == null) {\n      throw new IllegalArgumentException(\"The item must specify its mimeType\");\n    }\n    MediaMetadata metadata =\n        new MediaMetadata(\n            MimeTypes.isAudio(mediaItem.localConfiguration.mimeType)\n                ? MediaMetadata.MEDIA_TYPE_MUSIC_TRACK\n                : MediaMetadata.MEDIA_TYPE_MOVIE);\n    if (mediaItem.mediaMetadata.title != null) {\n      metadata.putString(MediaMetadata.KEY_TITLE, mediaItem.mediaMetadata.title.toString());\n    }\n    if (mediaItem.mediaMetadata.subtitle != null) {\n      metadata.putString(MediaMetadata.KEY_SUBTITLE, mediaItem.mediaMetadata.subtitle.toString());\n    }\n    if (mediaItem.mediaMetadata.artist != null) {\n      metadata.putString(MediaMetadata.KEY_ARTIST, mediaItem.mediaMetadata.artist.toString());\n    }\n    if (mediaItem.mediaMetadata.albumArtist != null) {\n      metadata.putString(\n          MediaMetadata.KEY_ALBUM_ARTIST, mediaItem.mediaMetadata.albumArtist.toString());","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/DefaultMediaItemConverter.java#L83-L119","documentation":"IllegalArgumentException thrown by DefaultMediaItemConverter.toMediaQueueItem when mediaItem.localConfiguration.mimeType is null. The Cast SDK requires a content type for MediaInfo, and unlike a player pipeline the Cast route has no sniffing step, so the item must carry an explicit MIME type. The converter also requires localConfiguration itself (non-null check above), i.e. a concrete URI.","triggerScenarios":"Building a MediaItem with only setUri() and passing it to a MediaItemConverter for cast queuing; MediaItem built from a MediaMetadata-only source; URI schemes where ExoPlayer would infer the type locally (progressive/adaptive) so the developer never set a MIME type until casting.","commonSituations":"App plays fine locally but crashes when the cast button routes the same MediaItem; items created from database rows with uri only; migrating to Media3 where setMimeType moved under RequestMetadata/LocalConfiguration.","solutions":["Set an explicit MIME type when building every castable item: MediaItem.Builder().setUri(...).setMimeType(MimeTypes.VIDEO_MP4)","Derive the type from the URI extension or your own manifest metadata when the source data lacks it","Use a custom MediaItemConverter if you must supply the type at conversion time rather than on every item","Guard before conversion: reject or enrich items whose localConfiguration.mimeType is null instead of crashing"],"exampleFix":"// before\nMediaItem item = new MediaItem.Builder().setUri(videoUrl).build();\nMediaQueueItem queueItem = converter.toMediaQueueItem(item); // throws\n\n// after\nMediaItem item = new MediaItem.Builder()\n    .setUri(videoUrl)\n    .setMimeType(videoUrl.endsWith(\".m3u8\") ? MimeTypes.APPLICATION_M3U8 : MimeTypes.VIDEO_MP4)\n    .build();\nMediaQueueItem queueItem = converter.toMediaQueueItem(item);","handlingStrategy":"validation","validationCode":"MediaItem safeForCast(MediaItem item, String uri) {\n  MediaItem.Builder b = item.buildUpon();\n  if (item.localConfiguration == null\n      || item.localConfiguration.mimeType == null) {\n    b.setMimeType(guessMimeType(uri)); // e.g. from extension: .mpd/.m3u8/.mp4/.mp3\n  }\n  return b.build();\n}\n\nstatic String guessMimeType(String uri) {\n  if (uri.endsWith(\".m3u8\")) return MimeTypes.APPLICATION_M3U8;\n  if (uri.endsWith(\".mpd\")) return MimeTypes.APPLICATION_MPD;\n  if (uri.endsWith(\".mp4\")) return MimeTypes.VIDEO_MP4;\n  return MimeTypes.VIDEO_MP4; // sensible default; adjust to your catalog\n}","typeGuard":null,"tryCatchPattern":"try {\n  MediaQueueItem queueItem = converter.toMediaQueueItem(item);\n} catch (IllegalArgumentException e) {\n  // item missing mimeType or localConfiguration: enrich and retry once\n  item = item.buildUpon().setMimeType(MimeTypes.VIDEO_MP4).build();\n  queueItem = converter.toMediaQueueItem(item);\n}","preventionTips":["Set setMimeType on every MediaItem at creation time — casting needs it even when local playback does not","Centralize MediaItem construction in a factory that always sets uri + mimeType","Prefer constant MimeTypes.* values over hand-written strings"],"tags":["android","cast","mediaitem","validation"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}