{"record":{"id":"1269df980b8c9791","repo":"google/ExoPlayer","slug":"failed-to-find-cue-point","errorCode":null,"errorMessage":"Failed to find cue point","messagePattern":"Failed to find cue point","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java","lineNumber":1281,"sourceCode":"              playerPositionUs, msToUs(contentDurationMs));\n    }\n    return adGroupIndex;\n  }\n\n  private int getAdGroupIndexForCuePointTimeSeconds(double cuePointTimeSeconds) {\n    // We receive initial cue points from IMA SDK as floats. This code replicates the same\n    // calculation used to populate adGroupTimesUs (having truncated input back to float, to avoid\n    // failures if the behavior of the IMA SDK changes to provide greater precision).\n    float cuePointTimeSecondsFloat = (float) cuePointTimeSeconds;\n    long adPodTimeUs = Math.round((double) cuePointTimeSecondsFloat * C.MICROS_PER_SECOND);\n    for (int adGroupIndex = 0; adGroupIndex < adPlaybackState.adGroupCount; adGroupIndex++) {\n      long adGroupTimeUs = adPlaybackState.getAdGroup(adGroupIndex).timeUs;\n      if (adGroupTimeUs != C.TIME_END_OF_SOURCE\n          && Math.abs(adGroupTimeUs - adPodTimeUs) < THRESHOLD_AD_MATCH_US) {\n        return adGroupIndex;\n      }\n    }\n    throw new IllegalStateException(\"Failed to find cue point\");\n  }\n\n  private String getAdMediaInfoString(@Nullable AdMediaInfo adMediaInfo) {\n    @Nullable AdInfo adInfo = adInfoByAdMediaInfo.get(adMediaInfo);\n    return \"AdMediaInfo[\"\n        + (adMediaInfo == null ? \"null\" : adMediaInfo.getUrl())\n        + \", \"\n        + adInfo\n        + \"]\";\n  }\n\n  private static long getContentPeriodPositionMs(\n      Player player, Timeline timeline, Timeline.Period period) {\n    long contentWindowPositionMs = player.getContentPosition();\n    if (timeline.isEmpty()) {\n      return contentWindowPositionMs;\n    } else {\n      return contentWindowPositionMs","sourceCodeStart":1263,"sourceCodeEnd":1299,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java#L1263-L1299","documentation":"Thrown by AdTagLoader when an IMA SDK cue point (an ad break time reported by the ads SDK) cannot be matched to any ad group in the current AdPlaybackState. The code converts the cue point to microseconds and searches for an ad group within THRESHOLD_AD_MATCH_US; if none matches, the mapping between what IMA reports and what ExoPlayer scheduled is broken, so it fails fast rather than misattributing ads.","triggerScenarios":"Calling the cue-point resolution path after IMA fires ADS_LOADER / content resume with cue points whose times differ from the adGroupTimesUs ExoPlayer derived at preparation time — e.g. the ad tag declares breaks at 15s but the IMA SDK later reports 15.05s beyond the match threshold, or ad groups were removed/modified concurrently.","commonSituations":"A mis-authored VAST/VMAP ad tag whose cue points drift from the declared break times; an IMA SDK version change that alters cue-point precision or timing; concurrent modification of AdPlaybackState (e.g. server-side ad insertion data merged while client-side cue points arrive); playlists where the same AdsLoader is reused across items with different ad break layouts.","solutions":["Verify the ad tag (VMAP break times) is self-consistent and does not declare near-duplicate breaks closer together than THRESHOLD_AD_MATCH_US.","Update the IMA SDK and ExoPlayer IMA extension to matching, current versions — precision and rounding behavior of cue points has changed across IMA releases.","Do not share one AdTagLoader/AdsLoader instance across media items with different ad configurations; release and recreate per stream.","If you construct AdPlaybackState yourself, ensure adGroupTimesUs are derived with the same float-truncation + Math.round(µs) math shown in the source, so IMA-reported cue points land within the threshold."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before resolving cue points, confirm a matching ad group exists:\nfloat cueFloat = (float) cuePointTimeSeconds;\nlong podUs = Math.round((double) cueFloat * C.MICROS_PER_SECOND);\nboolean matches = false;\nfor (int i = 0; i < adPlaybackState.adGroupCount; i++) {\n  long t = adPlaybackState.getAdGroup(i).timeUs;\n  if (t != C.TIME_END_OF_SOURCE && Math.abs(t - podUs) < 10_000L) matches = true;\n}\nif (!matches) Log.w(TAG, \"Cue point \" + podUs + \"us has no ad group; check ad tag\");","typeGuard":null,"tryCatchPattern":"try { int idx = resolveAdGroupIndexForCuePoint(cuePointTimeSeconds); } catch (IllegalStateException e) { Log.w(TAG, \"Cue point mismatch — verify ad tag consistency\", e); skipCuePoint(); }","preventionTips":["Derive adGroupTimesUs and IMA cue points from the same source math (float truncate + µs round).","Keep IMA SDK and ExoPlayer IMA extension versions in lockstep.","Never share one AdsLoader across items with different ad configurations."],"tags":["ima","ads","vast","playback-state","cue-points"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}