RocketChat/Rocket.Chat · warning
Failed to resume playback after URL recovery:
Error message
Failed to resume playback after URL recovery:
What it means
Raised inside the useReloadOnError media hook for audio/video file attachments: when the signed media URL expired, the hook fetched a fresh redirect URL, swapped node.src, and on 'canplay' called node.play() to resume at the previous position. The play() promise rejected (browser autoplay policy or a still-invalid URL), the rejection is caught and logged, and playback stays paused even though the stream itself recovered.
Source
Thrown at apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.ts:112
}
const wasPlaying = !node.paused;
const { currentTime } = node;
try {
const { redirectUrl: newUrl, expires: newExpiresAt } = await getRedirectURLInfo(url);
setExpiresAt(newExpiresAt);
node.src = newUrl || url;
const onCanPlay = async () => {
node.removeEventListener('canplay', onCanPlay);
node.currentTime = currentTime;
if (wasPlaying) {
try {
await node.play();
} catch (playError) {
console.warn('Failed to resume playback after URL recovery:', playError);
} finally {
isRecovering.current = false;
}
}
};
const onMetaDataLoaded = () => {
node.removeEventListener('loadedmetadata', onMetaDataLoaded);
isRecovering.current = false;
cleanup?.();
};
node.addEventListener('canplay', onCanPlay, { once: true });
node.addEventListener('loadedmetadata', onMetaDataLoaded, { once: true });
node.load();
} catch (err) {
console.error('Error during URL recovery:', err);
isRecovering.current = false;View on GitHub (pinned to b2c16d5842)
Solutions
- Catch NotAllowedError specifically and retry muted - muted autoplay is generally allowed - then surface an unmute control
- Otherwise render a resume/play button so a single user gesture resumes at node.currentTime
- Pre-emptively refresh the URL before expiresAt instead of waiting for the error/stalled event
- Verify the redirect endpoint still returns a fresh redirectUrl for the file when recovery runs
Example fix
// before
try {
await node.play();
} catch (playError) {
console.warn('Failed to resume playback after URL recovery:', playError);
}
// after
try {
await node.play();
} catch (playError) {
if ((playError as DOMException).name === 'NotAllowedError') {
node.muted = true;
try {
await node.play(); // muted autoplay is permitted
showUnmuteIndicator(node);
return;
} catch { /* fall through */ }
}
showResumeButton(node, currentTime); // one user gesture resumes playback
} Defensive patterns
Strategy: try-catch
Type guard
const isAutoplayBlocked = (error: unknown): error is DOMException => error instanceof DOMException && error.name === 'NotAllowedError';
Try / catch
try {
await node.play();
} catch (playError) {
if (isAutoplayBlocked(playError)) {
node.muted = true; // muted autoplay is permitted
await node.play().catch(() => showResumeButton(node));
} else {
showResumeButton(node); // NotSupportedError etc: URL still bad, let the user retry
}
} Prevention
- Treat every play() as fallible: always await it inside try/catch after swapping media sources
- Refresh expiring media URLs proactively using the expires value instead of waiting for error/stalled
- Remember the muted-autoplay escape hatch and pair it with an unmute affordance
When it happens
Trigger: Autoplay policy blocks programmatic play() without a user gesture after the source was reloaded (NotAllowedError in Chrome/Safari/Firefox), or the recovered URL is again invalid/expired (NotSupportedError). Only fires when the media was playing when the error/stall happened (wasPlaying true).
Common situations: Long-running tabs where S3 presigned/upload URLs expire; backgrounded browsers refusing unmuted autoplay; users who hit play, switched tabs, and the auto-recovery ran without a gesture; corporate browsers with strict autoplay settings.
Related errors
- Failed to start audio playback:
- Failed to resume audio playback:
- [Message Delivery] High delay detected: ${receiveDelay}ms. P
- Command does not exist in the system currently (or it is dis
- Invalid Slash Command parameter provided, it must be a valid
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/73bafd33d5136a07.
Report an issue: GitHub.