You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Library scans keep blowing out the heap and killing the process.
I started out at mem_limit: 512m. Bumping it to 1g helped but didn't actually fix anything, it still dies roughly once a day.
Reason the limit matters so much: Node 22 reads the cgroup limit and sets max-old-space-size to about half of it. So 512m gets you a ~256MB heap ceiling and 1g gets you ~524MB. Both are easy to hit.
What made this take me so long is that it doesn't look like a crash from the outside. docker inspect said ExitCode=0, OOMKilled=false, Health=healthy, RestartCount=0 the entire time. I had 60 restarts in 16 days and nothing in the docker state said why.
When I finally counted the two mechanisms separately over that window I got 59 V8 heap aborts and 4 cgroup OOM kills. The cgroup ones only show up in dmesg on the host, not inside the container. So it's almost entirely V8 doing the killing, the kernel is barely involved.
Couple of debugging notes since both of these wasted my time:
FATAL ERROR goes to stderr with no timestamp on it, so it doesn't interleave with seerr's normal timestamped output. grep -B3 FATAL gets you nothing useful. docker logs -t stamps everything including stderr, use that.
Don't grep for just FATAL. A lot of movie titles have "Fatal" in them. I got 16 hits and only 3 of them were real.
What I found
Radarr, Sonarr and Jellyfin scanners each pull a whole library in one call:
I measured the responses from inside the container. Radarr gives me 16,451
items, 79.7MiB on the wire, 75.8MiB once parsed. Here's a Radarr scan kicked off from a fresh restart:
BASELINE 165.4MiB
12:10:11 166.0MiB
12:10:22 499.6MiB <- one 10s sample later
12:10:33 243.6MiB
... sits at 250-275MiB for the rest of the scan
That's +335MiB inside a single sampling interval, right at the start, before it processes a single item. Makes sense, the parse is holding the raw response string and the object graph at the same time. On a 256MB ceiling that alone kills you. The Plex scanner doesn't do this, it pages through withpaginateLibrary.
Separate thing, and this one I understand less. RSS doesn't drop back down when a Plex Full Library Scan finishes:
01:27:14 178.5MiB scanning, movie libraries, flat
01:31:12 198.7MiB
01:54:01 Full Scan Complete
02:45:27 608.6MiB <- 50 minutes after it finished, nothing running
02:51:15 605.5MiB
(still around 621MiB at 09:00, 7+ hours later, idle)
So about 450MiB just stays there, and it's still there hours later.
That's what actually kills it, I think. The fetch spike on its own survived fine on a fresh process. But once seerr is already sitting at ~620MiB of leftovers, the next scheduled scan hits the same spike and there's no room for it:
10:00:00 radarr-scan fires (cron is 04:00 local)
10:00:09 FATAL ERROR: Reached heap limit <- 9 seconds in
10:00:13 Starting Seerr version 3.4.0
10:30:00 sonarr-scan fires (cron is 04:30 local)
10:34:25 FATAL ERROR: Ineffective mark-compacts near heap limit
10:34:29 Starting Seerr version 3.4.0
The numbers line up with that. From a clean 165MiB start, 165 + 335 is about 500, which fits under 524 and my manual Radarr scan survived fine. From 620MiB of leftovers, 620 + 335 is about 955, and the scheduled one died 9 seconds in, right at the fetch.
It's pretty repeatable too. Manual Sonarr scan died 4m50s in, the scheduled one died 4m25s in.
Fixing either one would probably stop my box crashing. But the leftover memory is what'll bite me again once the library gets bigger, so that's the one I care about more.
Stuff that might help
Page or stream the Radarr/Sonarr/Jellyfin fetches like paginateLibrary already does for Plex. Gets rid of the spike.
Figure out what a full scan is holding onto.
BUNDLE_SIZE in server/lib/scanners/baseScanner.ts is hardcoded to 20, and each bundle runs concurrently through Promise.all. There's no setting for it, so if you hit this there's no way to trade scan speed for memory. Only lever you've got is raising the container limit, and that just scales with library size. I noticed test: add scanner update rate override for testing #3241 adds an update rate override for testing, might be somewhere obvious to hang this off.
Whatever happens with the rest, a minimum mem_limit in the docs would help. The way Node sizes its heap makes 512m kind of a trap.
Happy to test patches, I've got a box that reproduces this every day.
Run seerr in docker on Node 22, mem_limit: 1g, no NODE_OPTIONS set.
Hook up a Radarr with a big library. Mine's 16,451 movies.
Check what heap ceiling you actually got: docker exec seerr node -e "console.log(require('v8').getHeapStatistics().heap_size_limit/1048576)"
512m gives ~256, 1g gives ~524.
Restart the container so you've got a clean baseline, then run Radarr Scan from Settings > Jobs with docker stats seerr open. RSS jumps by roughly the parsed size of your Radarr response within one sample.
For the actual crash: let it run a Plex Full Library Scan, check RSS is still high after Full Scan Complete, then fire off a Radarr or Sonarr scan. docker logs -t seerr gives you FATAL ERROR: Reached heap limit and thenStarting Seerr version 3.4.0 a few seconds later, while docker inspect is still telling you ExitCode=0 and OOMKilled=false.
Screenshots
n/a, server side
Logs
2026-07-29T10:00:09Z FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
2026-07-29T10:00:13Z [info]: Starting Seerr version 3.4.0
2026-07-29T10:34:25Z FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
2026-07-29T10:34:29Z [info]: Starting Seerr version 3.4.0
$ docker inspect seerr --format '{{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}}'
0 false 0
response sizes, measured from inside the container:
sonarr /api/v3/series: items=2352 wire=9.1MiB heapDeltaOnParse=8.6MiB
radarr /api/v3/movie: items=16451 wire=79.7MiB heapDeltaOnParse=75.8MiB
Platform
desktop
Database
SQLite (default)
Device
Beelink mini PC, running Proxmox, seerr is in docker inside an LXC
Operating System
Debian 12 (LXC), Docker 29.6.1, Node 22.22.2
Browser
Firefox
Additional Context
Image is ghcr.io/seerr-team/seerr:latest. Library sizes are 16,451 movies in Radarr, 2,352 series in Sonarr, and two Plex libraries, one movies one TV.
The bulk fetch thing I'm sure about. It lands in one sample, right when the scan kicks off, and the response size accounts for how big the jump is.
The leftover memory I'm much less sure about. I know it's there and I know it persists, but I didn't sample continuously through the whole scan so I can't tell you which part of it accumulates, and I haven't worked out what's actually being held. I did chase down two of my own guesses and both were wrong: AsyncLock cleans its keys up properly on release, and the recursive pagination doesn't seem to hold onto pages, since the Plex movie path runs that same code
and stays flat.
One last thing. Setting NODE_OPTIONS=--max-old-space-size=... on its own made things worse for me. All it did was turn a clean V8 abort I could grep for into
a kernel OOM kill, which was harder to work out. If you set it you have to move the container limit with it.
AI disclosure: I used an AI assistant to help collect and format the measurements here and to read through the scanner code. The numbers are all from my own instance, I ran them myself, and I've gone over everything above.
Description
Library scans keep blowing out the heap and killing the process.
I started out at
mem_limit: 512m. Bumping it to1ghelped but didn't actually fix anything, it still dies roughly once a day.Reason the limit matters so much: Node 22 reads the cgroup limit and sets
max-old-space-sizeto about half of it. So512mgets you a ~256MB heap ceiling and1ggets you ~524MB. Both are easy to hit.What made this take me so long is that it doesn't look like a crash from the outside.
docker inspectsaidExitCode=0,OOMKilled=false,Health=healthy,RestartCount=0the entire time. I had 60 restarts in 16 days and nothing in the docker state said why.When I finally counted the two mechanisms separately over that window I got 59 V8 heap aborts and 4 cgroup OOM kills. The cgroup ones only show up in
dmesgon the host, not inside the container. So it's almost entirely V8 doing the killing, the kernel is barely involved.Couple of debugging notes since both of these wasted my time:
FATAL ERRORgoes to stderr with no timestamp on it, so it doesn't interleave with seerr's normal timestamped output.grep -B3 FATALgets you nothing useful.docker logs -tstamps everything including stderr, use that.FATAL. A lot of movie titles have "Fatal" in them. I got 16 hits and only 3 of them were real.What I found
Radarr, Sonarr and Jellyfin scanners each pull a whole library in one call:
server/lib/scanners/radarr/index.ts:80-this.items = await this.radarrApi.getMovies()server/lib/scanners/sonarr/index.ts:88-this.items = await this.sonarrApi.getSeries()server/lib/scanners/jellyfin/index.ts:524-this.items = await this.jfClient.getLibraryContents(library.id)I measured the responses from inside the container. Radarr gives me 16,451
items, 79.7MiB on the wire, 75.8MiB once parsed. Here's a Radarr scan kicked off from a fresh restart:
That's +335MiB inside a single sampling interval, right at the start, before it processes a single item. Makes sense, the parse is holding the raw response string and the object graph at the same time. On a 256MB ceiling that alone kills you. The Plex scanner doesn't do this, it pages through with
paginateLibrary.Separate thing, and this one I understand less. RSS doesn't drop back down when a Plex Full Library Scan finishes:
So about 450MiB just stays there, and it's still there hours later.
That's what actually kills it, I think. The fetch spike on its own survived fine on a fresh process. But once seerr is already sitting at ~620MiB of leftovers, the next scheduled scan hits the same spike and there's no room for it:
The numbers line up with that. From a clean 165MiB start, 165 + 335 is about 500, which fits under 524 and my manual Radarr scan survived fine. From 620MiB of leftovers, 620 + 335 is about 955, and the scheduled one died 9 seconds in, right at the fetch.
It's pretty repeatable too. Manual Sonarr scan died 4m50s in, the scheduled one died 4m25s in.
Fixing either one would probably stop my box crashing. But the leftover memory is what'll bite me again once the library gets bigger, so that's the one I care about more.
Stuff that might help
paginateLibraryalready does for Plex. Gets rid of the spike.BUNDLE_SIZEinserver/lib/scanners/baseScanner.tsis hardcoded to20, and each bundle runs concurrently throughPromise.all. There's no setting for it, so if you hit this there's no way to trade scan speed for memory. Only lever you've got is raising the container limit, and that just scales with library size. I noticed test: add scanner update rate override for testing #3241 adds an update rate override for testing, might be somewhere obvious to hang this off.mem_limitin the docs would help. The way Node sizes its heap makes512mkind of a trap.Happy to test patches, I've got a box that reproduces this every day.
Version
3.4.0 (commit
2dbe8860179e83ed2044d5fc8ed17679ae05e5ad)Steps to Reproduce
mem_limit: 1g, noNODE_OPTIONSset.docker exec seerr node -e "console.log(require('v8').getHeapStatistics().heap_size_limit/1048576)"512m gives ~256, 1g gives ~524.
docker stats seerropen. RSS jumps by roughly the parsed size of your Radarr response within one sample.Full Scan Complete, then fire off a Radarr or Sonarr scan.docker logs -t seerrgives youFATAL ERROR: Reached heap limitand thenStarting Seerr version 3.4.0a few seconds later, whiledocker inspectis still telling youExitCode=0andOOMKilled=false.Screenshots
n/a, server side
Logs
Platform
desktop
Database
SQLite (default)
Device
Beelink mini PC, running Proxmox, seerr is in docker inside an LXC
Operating System
Debian 12 (LXC), Docker 29.6.1, Node 22.22.2
Browser
Firefox
Additional Context
Image is
ghcr.io/seerr-team/seerr:latest. Library sizes are 16,451 movies in Radarr, 2,352 series in Sonarr, and two Plex libraries, one movies one TV.The bulk fetch thing I'm sure about. It lands in one sample, right when the scan kicks off, and the response size accounts for how big the jump is.
The leftover memory I'm much less sure about. I know it's there and I know it persists, but I didn't sample continuously through the whole scan so I can't tell you which part of it accumulates, and I haven't worked out what's actually being held. I did chase down two of my own guesses and both were wrong:
AsyncLockcleans its keys up properly on release, and the recursive pagination doesn't seem to hold onto pages, since the Plex movie path runs that same codeand stays flat.
One last thing. Setting
NODE_OPTIONS=--max-old-space-size=...on its own made things worse for me. All it did was turn a clean V8 abort I could grep for intoa kernel OOM kill, which was harder to work out. If you set it you have to move the container limit with it.
AI disclosure: I used an AI assistant to help collect and format the measurements here and to read through the scanner code. The numbers are all from my own instance, I ran them myself, and I've gone over everything above.
Search Existing Issues
Closest I found was infinite loop and crash with "JavaScript heap out of memory" #513 from 2023, but that's a request approval loop, different thing. Couldn't find anything open about scanner memory.
Code of Conduct