149

I made a website where if the user clicks, it plays a sound. To prevent the sound from overlapping, I had to add the code:

n.pause();
n.currentTime = 0;
n.play();

But that causes the error: The play() request was interrupted by a call to pause()
To come up each time the sound event is triggered right after another trigger. The sounds still plays fine, but I want to prevent this error message constantly popping up. Any ideas?

jarrodwhitley
  • 826
  • 10
  • 29
Owen M
  • 2,585
  • 3
  • 17
  • 38
  • is it an error, or more of a notice? – dandavis Apr 22 '16 at 20:56
  • It's an error, here is the full error: Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause(). – Owen M Apr 22 '16 at 20:58
  • 7
    it's a newer bug, don't worry about it: https://bugs.chromium.org/p/chromium/issues/detail?id=593273 – dandavis Apr 23 '16 at 00:33
  • 3
    The simple fix for this is to just not call play or pause right after each other. Use the media events instead to determine when to pause or play. Example: `onCanPlay()`. All these answers are dirty hacks. – Martin Dawson Dec 23 '16 at 16:13
  • 1
    I had the opposite problem. I tried to `n.play()` **then** `n.pause()`. For that, [this Web Fundamentals article](https://developers.google.com/web/updates/2017/06/play-request-was-interrupted) describes the solution. tl;dr: `n.play().then(() => { n.pause()})` – totymedli Jul 05 '18 at 18:27
  • https://developer.chrome.com/blog/play-request-was-interrupted/ – Piet van Leeuwen Feb 21 '23 at 04:06

29 Answers29

117

I have encountered this issue recently as well - this could be a race condition between play() and pause(). It looks like there is a reference to this issue, or something related here.

As @Patrick points out, pause does not return a promise (or anything), so the above solution won't work. While MDN does not have docs on pause(), in the WC3 draft for Media Elements, it says:

media.pause()

Sets the paused attribute to true, loading the media resource if necessary.

So one might also check the paused attribute in their timeout callback.

Based on this great SO answer, here's a way you can check if the video is (or isn't) truly playing, so you can safely trigger a play() without the error.

var isPlaying = video.currentTime > 0 && !video.paused && !video.ended 
    && video.readyState > video.HAVE_CURRENT_DATA;

if (!isPlaying) {
  video.play();
}

Otherwise, @Patrick's answer should work.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
User 1058612
  • 3,739
  • 1
  • 27
  • 33
  • 1
    As @regency-software says, pause method doesn't return a Promise (there are no docs on .pause()), but just "undefined". So this solution can be applied only on play() then pause() cases. – Splact Jun 23 '16 at 15:10
  • Thanks for the info - I updated my answer to reflect what @regency software posted, which was correct, and he also had a working solution :-). – User 1058612 Jun 23 '16 at 16:20
  • how did you arrive at 150ms as the wait time? seems to be a chrome issue: https://bugs.chromium.org/p/chromium/issues/detail?id=593273 – connect2krish Sep 15 '16 at 20:29
  • See [@Regency Software](http://stackoverflow.com/users/3968481/regency-software)'s [answer](http://stackoverflow.com/a/37172024/1058612) for the timeout reason. I expanded on their answer, which was a good solution. Also, my answer references the link you provided at the beginning of the answer. – User 1058612 Sep 15 '16 at 21:50
  • This would not work for streaming video. If I have initializing WebRTC video, this still gives exception to the console. – Stepan Yakovenko May 11 '17 at 05:08
  • Why would it not work for streaming video? I'm not familiar with WebRTC streams, but certainly works with HLS for me. – User 1058612 May 11 '17 at 16:25
90

After hours of seaching and working, i found perfect solution.

// Initializing values
var isPlaying = true;

// On video playing toggle values
video.onplaying = function() {
    isPlaying = true;
};

// On video pause toggle values
video.onpause = function() {
    isPlaying = false;
};

// Play video function
async function playVid() {      
    if (video.paused && !isPlaying) {
        return video.play();
    }
} 

// Pause video function
function pauseVid() {     
    if (!video.paused && isPlaying) {
        video.pause();
    }
}

After that, you can toggle play/pause as fast as you can, it will work properly.

ffxsam
  • 26,428
  • 32
  • 94
  • 144
Nebojsa Sapic
  • 9,285
  • 1
  • 22
  • 23
  • 1
    This is how I achieved the problem. I feel this is a much better solution than relying on a timeout – Malcor Nov 15 '16 at 11:18
  • I used it [here](http://stefanomenci.com/pool/shotclock.html), but I had to change the name of two variables because the variable `onplaying` didn't work. I also had to use an mp4 as fallback because on my Android Notes5 the mpeg didn't work. – stenci Jan 19 '17 at 03:45
  • 1
    Why it's not marked as correct answer? Definitely this is a solution, not a hack with timeouts. – jeron-diovis Feb 17 '17 at 15:44
  • 17
    Why are two variables necessary? I'm not saying this is a bad solution. Just trying to grok it. – benevolentprof Mar 21 '17 at 15:31
  • 1
    Wouldn't this solution miss a few onclick events? – David Villamizar May 27 '19 at 02:18
  • 1
    If you call `playVid()` then quickly call `pauseVid()`, what happens? It looks like `isPlaying` will still be false, so `video.pause()` doesn't get called. The video would therefore play, even though the last thing I called was `pauseVid()`. Is that correct? – teedyay May 10 '21 at 16:02
25

I have hit this issue, and have a case where I needed to hit pause() then play() but when using pause().then() I get undefined.

I found that if I started play 150ms after pause it resolved the issue. (Hopefully Google fixes soon)

playerMP3.volume = 0;
playerMP3.pause();

//Avoid the Promise Error
setTimeout(function () {      
   playerMP3.play();
}, 150);
Elim Garak
  • 1,728
  • 1
  • 16
  • 21
  • If you mean the 150ms? I tried a few different values, and settled on this for my purpose. I was targeting Chrome and Android and it fit the needs of my app. It may be able to be lower. – Elim Garak May 21 '16 at 11:31
  • 3
    so it's almost random value as in not related to anything else than your impl. thanks for the clarification ! – y_nk May 23 '16 at 19:43
  • I'm curious are you using the same element to play multiple sounds? That would explain the load issue; one sound still loaded while trying to play another sound through the same element. A delay "could" stop it though its a cludgy fix and longer/shorter delays could be used for a better/best experience. Clearing and recreating elements on the fly has been my SOP which skips over a number of the audio/video and memory leaks. – tree Sep 13 '16 at 14:53
  • 1
    looks like a chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=593273 – connect2krish Sep 15 '16 at 20:29
  • 12
    Arbitrary setTimeout durations shouldn't be used to solve race conditions. – Gadget Blaster Oct 15 '16 at 20:38
  • Gadget provide a solution, or get Google to fix this issue that has been going on for months. – Elim Garak Oct 16 '16 at 00:33
  • Tree, I have 3 players running in this case: Sound Track, Subliminal Track, then I cross-fade the Sound Track with the same one to get a continuous play. – Elim Garak Oct 28 '16 at 13:10
9

I've just published an article about this exact issue at https://developers.google.com/web/updates/2017/06/play-request-was-interrupted that tells you exactly what is happening and how to fix it.

François Beaufort
  • 4,843
  • 3
  • 29
  • 38
8

try it

n.pause();
n.currentTime = 0;
var nopromise = {
   catch : new Function()
};
(n.play() || nopromise).catch(function(){}); ;
gest
  • 429
  • 6
  • 4
4

This solution helped me:

n.cloneNode(true).play();
Dmitry Kovganov
  • 143
  • 1
  • 6
2

Depending on how complex you want your solution, this may be useful:

var currentPromise=false;    //Keeps track of active Promise

function playAudio(n){
    if(!currentPromise){    //normal behavior
        n.pause();
        n.currentTime = 0;
        currentPromise = n.play();    //Calls play. Will store a Promise object in newer versions of chrome;
                                      //stores undefined in other browsers
        if(currentPromise){    //Promise exists
            currentPromise.then(function(){ //handle Promise completion
                promiseComplete(n);
            });
        }
    }else{    //Wait for promise to complete
        //Store additional information to be called
        currentPromise.calledAgain = true;
    }
}

function promiseComplete(n){
    var callAgain = currentPromise.calledAgain;    //get stored information
    currentPromise = false;    //reset currentPromise variable
    if(callAgain){
        playAudio(n);
    }
}

This is a bit overkill, but helps when handling a Promise in unique scenarios.

2

Solutions proposed here either didn't work for me or where to large, so I was looking for something else and found the solution proposed by @dighan on bountysource.com/issues/

So here is the code that solved my problem:

var media = document.getElementById("YourVideo");
const playPromise = media.play();
if (playPromise !== null){
    playPromise.catch(() => { media.play(); })
}

It still throws an error into console, but at least the video is playing :)

Sharpey
  • 400
  • 5
  • 11
2

Reason one - calling pause without waiting for play promise to resolve

too many answer fo this scenario, so I will just refer to the best doc for that issue:

https://developers.google.com/web/updates/2017/06/play-request-was-interrupted

Reason two - calling play when the tab is not focused

In this case, the browser could interrupt the play by calling pause when the tab is not focus. in order to save resources for the active tab.

So you could just wait for the tab to be focused before calling play:


async function waitForTabFocus() {
  return new Promise((resolve, reject) => {
    const onFocus = () => { resolve(); window.removeEventListener('focus', onFocus) };
    window.addEventListener('focus', onFocus)
  })
}
if (!document.hasFocus()) await this.waitForTabFocus();
videoEl.play();
jony89
  • 5,155
  • 3
  • 30
  • 40
2

I have a similar issue, I think doing something like this is the easiest :

video.play().then(() => {
    video.pause();
    video.currentTime = 0;
    video.play();
})

no matter if the video was playing or not, at the end the video will be paused without exception, then reset to zero and played again.

Calling play() even if the video is already playing is working fine, it returns a promise as expected.

Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86
Lk77
  • 2,203
  • 1
  • 10
  • 15
1

I've fixed it with some code bellow:

When you want play, use the following:

var video_play = $('#video-play');
video_play.on('canplay', function() {
 video_play.trigger('play');
});

Similarly, when you want pause:

var video_play = $('#video-play');
video_play.trigger('pause');

video_play.on('canplay', function() {
  video_play.trigger('pause');
});
Anand Singh
  • 1,091
  • 13
  • 21
Duc Nguyen
  • 530
  • 8
  • 16
1

Maybe a better solution for this as I figured out. Spec says as cited from @JohnnyCoder :

media.pause()

Sets the paused attribute to true, loading the media resource if necessary.

--> loading it

if (videoEl.readyState !== 4) {
    videoEl.load();
}
videoEl.play();

indicates the readiness state of the media HAVE_ENOUGH_DATA = 4

Basically only load the video if it is not already loaded. Mentioned error occurred for me, because video was not loaded. Maybe better than using a timeout.

1

removed all errors: (typescript)

audio.addEventListener('canplay', () => {
    audio.play();
    audio.pause();
    audio.removeEventListener('canplay');
}); 
ryanrain
  • 4,473
  • 3
  • 21
  • 21
1

With live streaming i was facing the same issue. and my fix is this. From html video TAG make sure to remove "autoplay" and use this below code to play.

if (Hls.isSupported()) {
            var video = document.getElementById('pgVideo');
            var hls = new Hls();
            hls.detachMedia();
            hls.loadSource('http://wpc.1445X.deltacdn.net/801885C/lft/apple/TSONY.m3u8');
            hls.attachMedia(video);
            hls.on(Hls.Events.MANIFEST_PARSED, function () {
                video.play();
            });
            hls.on(Hls.Events.ERROR, function (event, data) {
                if (data.fatal) {
                    switch (data.type) {
                        case Hls.ErrorTypes.NETWORK_ERROR:
                            // when try to recover network error
                            console.log("fatal network error encountered, try to recover");
                            hls.startLoad();
                            break;
                        case Hls.ErrorTypes.MEDIA_ERROR:
                            console.log("fatal media error encountered, try to recover");
                            hls.recoverMediaError();
                            break;
                        default:
                            // when cannot recover
                            hls.destroy();
                            break;
                    }
                }
            });
        }
1

It looks like a lot of programmers encountered this problem. a solution should be quite simple. media element return Promise from actions so

n.pause().then(function(){
    n.currentTime = 0;
    n.play();
})

should do the trick

pery mimon
  • 7,713
  • 6
  • 52
  • 57
1

Chrome returns a Promise in newest versions. Otherwise, simply:

        n.pause();
        n.currentTime = 0;
        setTimeout(function() {n.play()}, 0);
David
  • 623
  • 7
  • 16
1

I have the same issue, finally i solve by:

video.src = 'xxxxx';
video.load();
setTimeout(function() {
  video.play();
}, 0);
1

This piece of code fixed for me!

Modified code of @JohnnyCoder

HTML:

 <video id="captureVideoId" muted width="1280" height="768"></video>
                <video controls id="recordedVideoId" muted width="1280" 
 style="display:none;" height="768"></video>

JS:

  var recordedVideo = document.querySelector('video#recordedVideoId');
  var superBuffer = new Blob(recordedBlobs, { type: 'video/webm' });
  recordedVideo.src = window.URL.createObjectURL(superBuffer);
  // workaround for non-seekable video taken from
  // https://bugs.chromium.org/p/chromium/issues/detail?id=642012#c23
  recordedVideo.addEventListener('loadedmetadata', function () {
    if (recordedVideo.duration === Infinity) {
        recordedVideo.currentTime = 1e101;
        recordedVideo.ontimeupdate = function () {
            recordedVideo.currentTime = 0;
            recordedVideo.ontimeupdate = function () {
                delete recordedVideo.ontimeupdate;
                var isPlaying = recordedVideo.currentTime > 0 && 
     !recordedVideo.paused && !recordedVideo.ended && 
      recordedVideo.readyState > 2;
                if (isPlaying) {
                    recordedVideo.play();
                }
            };
        };
       }
      });
Eliotjse
  • 123
  • 1
  • 15
1

The cleanest and simplest solution:

var p = video.play();
if (p !== undefined) p.catch(function(){});
lapin
  • 2,098
  • 2
  • 21
  • 30
1

Here's my solution (tongue-in-cheek answer):

Sentry.init({
  // ...
  ignoreErrors: [
    'AbortError: The play() request was interrupted',
  ],
});

This error is pointless. If play() was interrupted, then it was interrupted. No need to throw an error about it.

ffxsam
  • 26,428
  • 32
  • 94
  • 144
  • And if you don't use Sentry? – Alexander Kucheryuk Feb 04 '22 at 14:46
  • My answer was tongue-in-cheek. ;) I haven't really found a good solution for this. If you don't use Sentry or any kind of error logging, then there's nothing to do, since this error doesn't throw an error to the user. But services like Sentry and Rollbar tend to over-report errors, in my experience, so many of these can be ignored. – ffxsam Feb 04 '22 at 19:50
  • Very useful thanks ! I didn't know we can ignore errors like that – Lk77 Sep 01 '22 at 07:43
  • Still came up in my google search for a "sentry" error about this, thats a win for me, thanks. – Kyzer Apr 17 '23 at 15:51
0

Here is another solution if the reason is that your video download is super slow and the video hasn't buffered:

if (videoElement.state.paused) { videoElement.play(); } else if (!isNaN(videoElement.state.duration)) { videoElement.pause(); }

Taysky
  • 4,331
  • 2
  • 20
  • 28
0

here is a solution from googler blog:

var video = document.getElementById('#video')
var promise = video.play()
//chrome version 53+
if(promise){
    promise.then(_=>{
        video.pause()
    })
}else{
    video.addEventListener('canplaythrough', _=>{
        video.pause()
    }, false)
}
stackFish
  • 597
  • 4
  • 12
0

All new browser support video to be auto-played with being muted only so please put Something like the this

<video autoplay muted="muted" loop id="myVideo">
  <source src="https://w.r.glob.net/Coastline-3581.mp4" type="video/mp4">
</video>

URL of video should match the SSL status if your site is running with https then video URL should also in https and same for HTTP

Shobhit Verma
  • 794
  • 8
  • 25
0

In my case, I was getting an error while changing the video src. When a user tried to play different videos very rapidly, the error would popup. I managed to solve this with a small delay on .play(), i.e.:

video_player.pause();
video_player.removeAttribute('src'); // empty source
video_player.load();
video_player.src = video_url;
setTimeout(function(){video_player.play()},300);

I've tried, to some extended, emulating user behavior of changing videos rapidly and the error disappeared.
Not sure if this solution is a OSFA solution, but it worked for me.

Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268
-1

I ran into the same issue and resolved it by dynamically adding the autoplay attribute rather than using play(). That way the browser figured out to play without running into the race condition.

Jannis Hell
  • 736
  • 8
  • 17
-1

Trying to get an autoplaying video to loop by calling play() when it ends, the timeout workaround did not work for me (however long the timeout is).

But I discovered that by cloning/replacing the video with jQuery when it ended, it would loop properly.

For example:

<div class="container">
  <video autoplay>
    <source src="video.mp4" type="video/mp4">
  </video>
</div>

and

$(document).ready(function(){
  function bindReplay($video) {
    $video.on('ended', function(e){
      $video.remove();
      $video = $video.clone();
      bindReplay($video);
      $('.container').append($video);
    });
  }
  var $video = $('.container video');
  bindReplay($video);
});

I'm using Chrome 54.0.2840.59 (64-bit) / OS X 10.11.6

Silvain
  • 399
  • 4
  • 20
-1

I think they updated the html5 video and deprecated some codecs. It worked for me after removing the codecs.

In the below example:

<video>
    <source src="sample-clip.mp4" type="video/mp4; codecs='avc1.42E01E, mp4a.40.2'">
    <source src="sample-clip.webm" type="video/webm; codecs='vp8, vorbis'"> 
</video>

    must be changed to

<video>
    <source src="sample-clip.mp4" type="video/mp4">
    <source src="sample-clip.webm" type="video/webm">
</video>
Amr
  • 574
  • 1
  • 11
  • 15
-1

When you see an error with Uncaught (in promise) This just means that you need to handle the promise with a .catch() In this case, .play() returns a promise. You can decide if you want to log a message, run some code, or do nothing, but as long as you have the .catch() the error will go away.

var n = new Audio();
n.pause();
n.currentTime = 0;
n.play().catch(function(e) {
  // console.log('There was an error', e);
});
seantomburke
  • 10,514
  • 3
  • 18
  • 23
  • it doesn't return a promise – Martin Dawson Dec 23 '16 at 12:28
  • @MartinMazzaDawson It does return a promise. If you look at xxx's clarifying comment in the question he says "It's an error, here is the full error: Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause()." – seantomburke Jan 03 '17 at 22:09
-5

I have used a trick to counter this issue. Define a global variable var audio;

and in the function check

if(audio === undefined)
{
   audio = new Audio(url);
}

and in the stop function

audio.pause();
audio = undefined;

so the next call of audio.play, audio will be ready from '0' currentTime

I used

audio.pause();
audio.currentTime =0.0; 

but it didn't work. Thanks.

batman
  • 1,937
  • 2
  • 22
  • 41
Gippy Aulakh
  • 104
  • 7