How7o
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Reading: Fix “URL.createObjectURL is not a function” in Chrome Extension Service Workers
Share
How7oHow7o
Font ResizerAa
  • OS
Search
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Follow US
© 2024–2026 How7o. All rights reserved.
How7o > Free Laravel, PHP, WordPress & Server Tutorials > Web Development > Fix “URL.createObjectURL is not a function” in Chrome Extension Service Workers
Web Development

Fix “URL.createObjectURL is not a function” in Chrome Extension Service Workers

how7o
By how7o
Last updated: May 23, 2026
5 Min Read
URL.createObjectURL not a function — Chrome extension service worker fix
SHARE

If you see TypeError: URL.createObjectURL is not a function in a Chrome extension service worker, the cause is straightforward: that API isn’t available in service-worker contexts. Manifest V3 replaced the background page (which had a DOM) with a service worker (which doesn’t), and URL.createObjectURL requires a document. Switch to FileReader.readAsDataURL to make a data: URL instead, which works without a DOM.

Contents
  • The broken version
  • The fix — use a data URL
  • Alternative — let Chrome download the URL directly
  • If you really need a blob URL — do it from a content script
  • Frequently asked questions
  • Related guides
  • References

Last verified: 2026-05-17 with Chrome 124 (Manifest V3). Originally published 2022-09-16, rewritten and updated 2026-05-17.

The broken version

// In background.js (MV3 service worker)
fetch(url).then(data => {
    let dataUrl = URL.createObjectURL(data.blob());   // TypeError
    chrome.downloads.download({ url: dataUrl, filename: 'image.png' });
});

Two bugs: data.blob() returns a promise that has to be awaited, and URL.createObjectURL isn’t available in service workers.

MV3 service worker — no URL.createObjectURL, use FileReader.readAsDataURL for data: URL, content script for blob URL

The fix — use a data URL

async function downloadImage(url, filename) {
    const res  = await fetch(url);
    const blob = await res.blob();

    const reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onloadend = () => {
        chrome.downloads.download({
            url:      reader.result,    // "data:image/png;base64,..."
            filename: filename || 'image.png',
        });
    };
}

FileReader.readAsDataURL produces a complete data: URL — a base64-encoded inline representation of the blob. The result works wherever a URL string works, including the url parameter of chrome.downloads.download. No DOM required.

Alternative — let Chrome download the URL directly

// If you don't need to inspect or modify the bytes, just pass the URL straight through
chrome.downloads.download({
    url:      'https://example.com/image.png',
    filename: 'image.png',
});

The downloads API does its own fetching — for a public URL, skipping the fetch+blob round-trip avoids the problem entirely.

If you really need a blob URL — do it from a content script

// content.js — runs in the page context, has a DOM
const res  = await fetch(url);
const blob = await res.blob();
const blobUrl = URL.createObjectURL(blob);

// Then pass it back to the service worker if it needs to act on it
chrome.runtime.sendMessage({ action: 'download', url: blobUrl });

Blob URLs are scoped to the document that created them — so the content script must be the one that calls chrome.downloads.download, or wire the actual download up through a message-passed proxy.

Frequently asked questions

Why isn’t URL.createObjectURL available in MV3 service workers?

Manifest V3 replaced the background page with a service worker, which doesn’t have a DOM. URL.createObjectURL requires a document context to produce blob URLs scoped to a window — service workers have no window, so the function is undefined. Use a data: URL (from FileReader.readAsDataURL) or do the work from a content script / popup that does have a document.

What’s the difference between a blob URL and a data URL?

A blob URL (blob:...) is a pointer to in-memory data, scoped to the document that created it; the actual bytes never leave the browser. A data URL (data:image/png;base64,...) embeds the bytes inline as base64-encoded text. Data URLs are bigger (~33% overhead) but self-contained — they work anywhere a URL works, including in MV3 service workers.

Will this work for large files?

Yes, but a data URL inflates the size by ~33%. For multi-MB files, the encoded string can get large enough to stress memory in a service worker. If you’re downloading something huge, consider doing the download from a content script (which has a document and can use URL.createObjectURL) or pass the blob to chrome.downloads.download via a more recent API. For most images, the data-URL approach is fine.

Where should I do this — background script or content script?

MV3 service worker for headless background work (cron-like scheduled tasks, network calls). Content script for anything that needs DOM access (parsing pages, blob URLs). Popup for UI-driven actions. Move the function that creates the URL into a context where the right APIs exist.

Related guides

  • How to Convert an Image to a Base64 String in JavaScript
  • How to Get a Website’s Favicon URL with JavaScript
  • How to Remove the “Other Favorites” Button in Microsoft Edge

References

Chrome extensions: Migrating to Manifest V3: developer.chrome.com/docs/extensions/develop/migrate. chrome.downloads: developer.chrome.com/docs/extensions/reference/api/downloads. MDN FileReader.readAsDataURL: developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL.

TAGGED:browser-extensionchromeJavaScript

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
[mc4wp_form]
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Copy Link Print
Previous Article Fix CSS page breaks not working with HTML tables Why CSS Page Breaks Don’t Work in HTML Tables (and How to Fix It)
Next Article yum/dnf -y flag explained What Does the -y Flag Do in yum / dnf / apt-get?
Leave a Comment

Leave a Reply Cancel reply

You must be logged in to post a comment.

FacebookLike
XFollow
PinterestPin
InstagramFollow
Most Popular
Run Laravel queue workers with Supervisor
How to Run Laravel Queue Workers in Production with Supervisor
May 23, 2026
Nginx as a reverse proxy for a Node.js app on Ubuntu
How to Set Up Nginx as a Reverse Proxy for Node.js on Ubuntu
May 23, 2026
Install and configure Redis on Ubuntu for Laravel and WordPress
How to Install and Configure Redis on Ubuntu (for Laravel & WordPress)
May 23, 2026
Harden a fresh Ubuntu VPS with UFW, Fail2Ban, and SSH key auth
How to Harden a Fresh Ubuntu VPS: UFW + Fail2Ban + SSH Key Auth
May 23, 2026
Set up Let's Encrypt SSL with Certbot on Ubuntu
How to Set Up Let’s Encrypt SSL with Certbot on Ubuntu (Apache & Nginx)
May 23, 2026

You Might Also Like

WordPress logged-in menu swap — register_nav_menus + wp_nav_menu with is_user_logged_in ternary
Web Development

How to Display Different Menus to Logged-In Users in WordPress

7 Min Read
WooCommerce homepage filter to hide out of stock products
Web Development

Hide Out of Stock Products from Homepage in WooCommerce (Keep Them Visible Elsewhere)

5 Min Read
Laravel DataTables HTML column — rawColumns opt-out of the default escaping
Web Development

How to Add an HTML Column in Laravel DataTables

7 Min Read
WordPress pre_get_posts scoped to a custom post type with is_post_type_archive
Web Development

How to Apply pre_get_posts on Custom Post Types in WordPress

8 Min Read
How7o

We provide tips, tricks, and advice for improving websites and doing better search.

Tools

  • Age Calculator
  • Word Counter
  • Image Upscaler
  • Password Generator
  • QR Code Generator
  • See all tools→

Pranks

  • Fake Blue Screen Prank
  • Hacker Typer
  • Fake iMessage Generator
  • Windows XP Crash Prank
  • Windows 11 Update Prank
  • See all prank screens →

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service
  • Sitemap
© 2024–2026 How7o. All rights reserved.
Welcome Back!

Sign in to your account

Username or Email Address
Password

Lost your password?