Chrome alarms are very unreliable for timer extentions + some other suggestions (offscreen etc)

387 views
Skip to first unread message

J Benjamin Dsouza

unread,
Jul 27, 2026, 2:35:04 AMJul 27
to Chromium Extensions

Hello!

I have a focus timer/website blocker extension. But this post is mostly about the focus timer.

Before manifest v3, it was very easy to run a long-running task such as a setInterval, as it persisted. But after v3, it caused a lot of issues since the lifecycle of the worker is short-lived; APIs like setInterval wouldn’t work as expected.

Since then, I’ve been using alarms, and here are a few observations

  1. They’re very unreliable and have a lot of drift
  2. You can’t run them every second
  3. Experience between the unpacked version and the runtime prod versions is very different.

I’m aware all these are mentioned in the docs, but I have a proposal

Issue 1 and Issue 2

While I understand alarms can be very heavy on the user's system and running them each second requires significant memory and causes perf issues, we need something better, something more accurate.

Currently, even scheduling a single alarm, 15m or 25m in the future, it fires unreliably. Always firing a few seconds late.

My suggestion is that even if you don’t support minute accuracy, it would be nice to have accurate scheduling. Just throwing some ideas around: maybe allow for a pool of 5-6 accurate schedules per hour, or allocate some memory or give the devs access to a queue (with limits), that they can push schedules into?

Issue 3

Testing alarms is a pain on unpacked version and runtime prod version. Everything seems to work really well on the local machine, but as soon as you try the live runtime version, everything breaks. It becomes really hard to pinpoint issues when alarms are involved. On local versions, Chrome lets you run alarms up to minute accuracy. This isn’t the case on prod.

My suggestion would be to have some kind of flag that allows you to run the exact runtime env for unpacked versions. That way it becomes easier to debug alarm drift/delay issues.

My workaround

Right now, my workaround is a bit wonky. I’m using offscreen and alarms to run my timer. And I’m being very pragmatic with my code; I only run the alarms once a minute (since I don’t care when it fires if its not < 60s), and once the timer duration reaches < 60, i hand the controls over to an offscreen which then runs a setInterval. I want the ending to be very accurate and fire exactly at the end time.

Before using this workaround, the alarms would fire at unreliable end times many seconds after the actual timer had ended. This caused a lot of my users to think the extension is broken (cause it stopped at 0:00 and didn’t end the timer session). And it was really hard to pinpoint this issue because it worked fine unpacked. I have tested this on the runtime version, and it seems to hold. I’m using “WORKER” reason for the offscreen, as it keeps the offscreen open indefinitely.

My suggestion would be to introduce another official reason called “INTERVAL” or “TIMER”.


TLDR using claude
  • Alarm accuracy/drift: Even single alarms scheduled minutes out fire several seconds late, with no way to tighten this. Not asking for full setInterval-level precision — just a bounded way to get more accurate scheduling (e.g. a small quota of high-precision timers per hour) without opening up unrestricted per-second alarms.
  • Unpacked vs. packed behavior diverges: Unpacked extensions have no alarm throttling, so drift/timing bugs are invisible locally and only show up once published. Need a way to simulate prod throttling while testing unpacked, so this class of bug is debuggable before shipping.
  • No official "keep this context alive for a timer" pattern: Currently working around this with an offscreen document using the WORKERS reason to run setInterval for the final accurate countdown. Suggest formalizing this with a dedicated offscreen reason (e.g. TIMER/INTERVAL) so it's a documented, sanctioned pattern instead of a repurposed workaround.

Anyways that's all I can think of right now. Hope you can consider my suggestions!

P.S: Chrome store reviews got faster; thanks to that, I was able to push multiple hot fixes for my extension. Please convey my thanks to the review team.

-Ben

Oliver Dunk

unread,
Jul 27, 2026, 4:17:38 AMJul 27
to J Benjamin Dsouza, Chromium Extensions
Hi Ben,

Making the alarms API more reliable has been a focus of mine, so you have my attention. That said, I am a little unsure how much of what you have mentioned is unexpected / would be something we are likely to change.

Currently, even scheduling a single alarm, 15m or 25m in the future, it fires unreliably. Always firing a few seconds late.

Is this really a problem? Could you share your use case? I'm not aware of any intentional delay, but I can imagine it taking a few seconds if the service worker needs to be started depending on your machine specs. You later say "I don’t care when it fires if its not < 60s" which makes it sound like perhaps this isn't too important.

On local versions, Chrome lets you run alarms up to minute accuracy. This isn’t the case on prod.

Alarms are just as accurate for packed and unpacked extensions, although in packed extensions the minimum interval for an alarm is 30 seconds. Does this match what you are seeing or are you seeing other signs that the accuracy is lower than expected?

Thanks for the kind words on the review process - I will pass those on!
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/b5353c0f-a775-4edb-899e-74e5c9e70986n%40chromium.org.

J Benjamin Dsouza

unread,
Jul 27, 2026, 5:07:07 AMJul 27
to Chromium Extensions, Oliver Dunk, Chromium Extensions, J Benjamin Dsouza

>  I am a little unsure how much of what you have mentioned is unexpected / would be something we are likely to change.

I can only speak from my experience as I have no idea how the internals of Chrome or alarms work. But from what I've observed, at least in the older versions of my extention (until yday), is that only using alarms for high accuracy tasks/calls is very unreliable, as they fire a little late. Sometimes they work normally, but most times theres a delay of 15-30s. 

In old versions of my extension (until i did a rewrite last week), the alarms used to run every second. Unpacked, this wasn't a problem as Chrome runs it upto per second accuracy. So my timer worked fine. The problem arises when it's shipped to the Chrome Store; the runtime versions get throttled significantly. So I'm assuming that's why there's a delay or drift with the timer. 

Some Context about my extention,

And all this has to do with how my extension works. Until recently, I was using counters + alarms to measure time in my extension; even though unreliable, it didn't cause much issue (I was running alarms each second using alarms.when). The only issue was drift and because of this, it didn't bother me that the timers fired slowly (the timer always turns off automatically, regardless of drift, if the counter reaches 0). Then I made the change to make it more accurate; now it uses Date.now() to measure the clock (start time and endtime). When I pushed to the Chrome Store, and tested the live version, even though the timer was now accurate (I was running the alarms every 10 seconds), the last one always missed or was delayed by a few seconds. 

I tried scheduling an alarm to run at a specific time, say n + 25m also fails at runtime (works fined in unpacked). Always delayed by 10-15 seconds. I don't know why this happens. Lots of my users had raised this issue too.

> You later say "I don’t care when it fires if its not < 60s" which makes it sound like perhaps this isn't too important.

Yes, I mean for most of the timer's duration it doesn't matter. As I only use it to update the Chrome badge to show time (15m, 25m etc). But the last 60 seconds of a session matter, as when the clock ends, it fires a message to display a dialog box. Only using alarms makes this event fire a lot later than it should. So offscreen, fires this event accurately and ends the timer.

For context, this is my extension (it works now cuz the last 60s countdown is passed of to offscript)


Set a timer for 00:25, (uses the offscreen controller to count time). Then try 5:00; it uses alarms that fire once per min, switches to offscreen when <1:00.

I hope all this makes sense. Happy to clarify some more!

Ben

Mythical 5th

unread,
Jul 27, 2026, 11:33:06 AMJul 27
to Chromium Extensions, J Benjamin Dsouza, Oliver Dunk, Chromium Extensions
Oliver,

You might be interested in an issue I opened in February, which shows alarms being affected by other extensions: https://issues.chromium.org/issues/486562286

In my experience alarms are accurate, but I can reliably cause a 30-second delay in a 30-second alarm by installing a particular extension from the webstore. The issue mentioned above has a test case attached to it. I ran that just now in a newly created Chrome Stable profile and found the alarms to be delayed by no more than 0.002 seconds, until I installed the other extension. Every 30-second alarm created afterwards was delayed by about 30 seconds.

Oliver Dunk

unread,
Jul 27, 2026, 1:16:29 PMJul 27
to Mythical 5th, Chromium Extensions, J Benjamin Dsouza
I tried scheduling an alarm to run at a specific time, say n + 25m also fails at runtime (works fined in unpacked). Always delayed by 10-15 seconds. I don't know why this happens. Lots of my users had raised this issue too.
In my experience alarms are accurate, but I can reliably cause a 30-second delay in a 30-second alarm by installing a particular extension from the webstore.

In both of your cases, I think that while the behavior is unintuitive, it isn't unexpected for our current implementation.

Ignoring unpacked extensions for a moment - when an alarm fires (say, Session Buddy firing the alarm it registers to run once every minute), we determine the next alarm to run. We then schedule the next time we need to fire alarms, but notably, we take max(scheduled_time, 30 seconds). If your alarm was due to fire in 1s, we actually wait 30s and then fire it. Over time this drift builds up and can cause alarms to be up to 30 seconds delayed.

The best way to fix this would likely be for us to change Chromium to have each extension run its own independent set of timers. However, there is other work in discussion that would make this unnecessary. Specifically, in the past we have discussed reducing the minimum alarm interval from 30s to 1s. Making this change would be a lot easier and make the quirk a lot less noticeable, so I think it is the best move. That is blocked on doing some work to make sure the performance implications from less well coded extensions wouldn't be too significant. This has been on my plate for a while and I will take this email as a reminder to take another look.

Then try 5:00; it uses alarms that fire once per min, switches to offscreen when <1:00.

Smart! One thing you could try is switching to setInterval in your service worker at <1:00 to avoid needing to create a service worker. As long as you call an extension API in your service worker at least every 30 seconds we won't terminate it for inactivity.

Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

Mythical 5th

unread,
Jul 27, 2026, 3:15:41 PMJul 27
to Chromium Extensions, Oliver Dunk, Chromium Extensions, J Benjamin Dsouza, Mythical 5th
Thanks, Oliver.

I expect the Web Store's extension reviewers take a dim view of extensions which use trickery to keep a service worker alive. If so, for how long can we preserve our service worker without raising a flag? Specifically, my timer extension has a maximum countdown time of 9 minutes, and it would be nice to go straight to setInterval() instead of creating an alarm and programming a hand-over.

J Benjamin Dsouza

unread,
Jul 28, 2026, 1:54:03 PMJul 28
to Chromium Extensions, Mythical 5th, Oliver Dunk, Chromium Extensions, J Benjamin Dsouza
Thanks for your reply, Oliver!

Ah, yeah, changing the underlying stuff seems like a lot of work. But I guess that's fine since my workaround works for me and I don't need the alarm to run every second for my case. I don't think many extensions need to run every second unless they are timers.


> Smart! One thing you could try is switching to setInterval in your service worker at <1:00 to avoid needing to create a service worker. As long as you call an extension API in your service worker at least every 30 seconds we won't terminate it for inactivity.

I have this fear that service workers are very unreliable haha! I'll stick to the offscreen doc for now, as it works reliably for my case. Although one thing I'd request is to have an official reason called TIMER/INTERVAL (which would run indefinitely) for the justification for creating an offscreen doc. Right now, I'm using the WORKER reason, but maybe in the future things change, and this reason wouldn't be valid, or it gets closed early or something like that. 

Ben

Anton Bershanskyi

unread,
Jul 30, 2026, 9:35:35 AMJul 30
to Chromium Extensions, J Benjamin Dsouza, Mythical 5th, Oliver Dunk, Chromium Extensions
 Hi Benjamin,

you identified a few separate issues with Alarms API, some of which stem from lacking (or outright missing) specifications and some stem from limitations of (Chromium) implementation. Also, you correctly identified that Alarms API does not provide a way to specify desired priority (which would be nice since some tasks require immediate execution and some are low-priority).

In a nut shell, Alarms API orchestrates start-up of extension execution environments, but the actual work within these execution contexts can be scheduled with usual web scheduling APIs like `setInterval()` and `setTimeout()`.

> Right now, my workaround is a bit wonky. I’m using offscreen and alarms to run my timer. …  I only run the alarms once a minute (since I don’t care when it fires if its not < 60s), and once the timer duration reaches < 60, i hand the controls over to an offscreen which then runs a setInterval.

This sounds about right: Alarms API starts up an execution context and then actual work occurs in said execution context. My only questions are: why did you choose to offscreen document context? What actual work does it perform? Would it be appropriate to integrate it into the content page via a content script?

In theory:

All alarms in JavaScript are expected to consistently be late, but they should not exhibit any major accumulated drift if used appropriately. JavaScript approach to scheduling is to always be late: `setInterval()` and `setTimeout()` events fire later than the time they are scheduled for because `setInterval()` and `setTimeout()` only create an event on the event stack and then the actual event occurs when event loop finishes the current task. This model permits a great deal of async programming, but notoriously suffers from the problem of long-running tasks. Notably, whole language features evolved to fix this problem. E.g., `yield` which allows developers to literally break up a long-running task to permit some high-priority work. Also, workers were explicitly invented to fix the single-threaded nature of JavaScript and permit multiple threads to make progress, even if both threads run on the same CPU.
Extension alarms scheduling logic polls past alarms (in AlarmManager::PollAlarms() only after their time already expired), which is consistent with `setInterval()` and `setTimeout()`, as described above. However, `setInterval()` and `setTimeout()` run within the same context that processes logic triggered by them and are designed to have minimal overhead. By contrast, extension Alarms API was designed to wake up lazy non-persistent background pages in MV2 and to start up background service workers in MV3, so by definition Alarms API can not run in the same execution context it creates, since said context might not even exist. Also, Alarms API writes data to disk after every alarm trigger to persist alarms across whole browser restarts, while  `setInterval()` and `setTimeout()` operate entirely within RAM .

Thus, even in theory Alarms API should not be expected to be as light-weight as `setInterval()` and `setTimeout()`. There were discussions to add Alarms API with relaxed or removed disc persistence guarantees and make some calls equivalent to `setInterval()` and `setTimeout()`, but so far nothing came out of it.

In practice:

Every time Chrome delivers an alarm, it writes updated alarm state onto disc. These writes can have performance impact, so Chrome introduced a JavaScript-like de-bouncing mechanism, limited the total number of alarms to 500 alarms per extension, and limited the maximum size of alarm name to 1024 bytes. These measures were effective for limiting amount of disk I/O, but they did not address the IPC latency and time for extension context startup and data delivery, so extensions should never be expected to be timely, unfortunately. The only reasonable approach is to schedule alarms via Alarms API a bit in advance to pre-load execution context and and then wait for exact time in already prepared context.

woxxom

unread,
Jul 31, 2026, 1:58:44 AMJul 31
to Chromium Extensions, Anton Bershanskyi, J Benjamin Dsouza, Mythical 5th, Oliver Dunk, Chromium Extensions
Anton Bershanskyi, but literally none of that explains delays of "many seconds"...


> integrate it into the content page via a content script?

FWIW, not that it's a frequent concern but timers in the webpage can be cleared by any script calling clearInterval or clearTimeout in a loop from 0 to the last result of setInterval or setTimeout.

J Benjamin Dsouza

unread,
Aug 3, 2026, 6:23:57 PM (13 days ago) Aug 3
to Chromium Extensions, Anton Bershanskyi, J Benjamin Dsouza
Thanks for the write up Anton!

> My only questions are: why did you choose to offscreen document context? What actual work does it perform? Would it be appropriate to integrate it into the content page via a content script?

For my specific use case, offscreen is the most reliable. Content page and script only work if the tab and website are loaded, and it would be really annoying to inject a global timer tracker into every tab/page available, which would also introduce a lot of edge cases and bugs.

The offscreen, in my case, basically acts as a more accurate timer that never misses firing an event, without which most of my users think my app is broken. With alarms, there's always a delay in firing this event, which makes the injected UI (via content script) or popup appear stuck until the final event is fired.

Hope this answers your question! :)
- Ben

Oliver Dunk

unread,
Aug 13, 2026, 6:22:53 AM (3 days ago) Aug 13
to J Benjamin Dsouza, Chromium Extensions, Anton Bershanskyi
Thanks all for the great feedback in this thread. We've just made two related changes based on the discussion.

Firstly, in https://crrev.com/c/8219731, we made throttling for alarms per-extension.  This means alarms firing in one extension should no longer affect the timing of alarms in another, and should prevent the drift that some developers were seeing.

Second, in https://crrev.com/c/8248259, we've made it so packed extensions can also schedule alarms as frequently as every 1s. This should reduce the need to switch between the alarms API and other web APIs when implementing a high frequency timer.

The first change is available in Chrome Canary now, and the second should be available in the next release (within 24 hours). These changes are in Chrome 153 and you can see the release schedule here: https://chromiumdash.appspot.com/schedule
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

Mythical 5th

unread,
Aug 13, 2026, 7:14:34 AM (3 days ago) Aug 13
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Anton Bershanskyi, J Benjamin Dsouza
Thanks, Oliver. My extension is working in Canary now.

Will there be a note in the documentation? "Prior to version 153..."
https://developer.chrome.com/docs/extensions/reference/api/alarms#method-create

Oliver Dunk

unread,
Aug 13, 2026, 10:53:42 AM (3 days ago) Aug 13
to Mythical 5th, Chromium Extensions, Anton Bershanskyi, J Benjamin Dsouza
Yes, definitely. Generally we try to update the documentation when a change lands in beta.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

Reply all
Reply to author
Forward
0 new messages