Your scheduled core:archive job is doing one of two infuriating things. Either the log fills with Got invalid response from API request: ...&trigger=archivephp. The response was empty. This usually means a server error, or it runs clean, prints Processed 0 archives, and your dashboards stay frozen on yesterday's numbers while live visits keep arriving. Tracking isn't broken. Flip "Archive reports when viewed from the browser" back to Yes and the data shows up instantly, which proves the raw data is already in the database. What's broken is archiving, and the two symptoms have different causes even though they land in the same place.
These are two separate problems with the same symptom. Got invalid response from API request means an archiving worker died before it could report back, so run core:archive -vvv by hand and read the log for the path that failed: the CLI PHP error log for a subprocess, or the web server log and matomo.log for an HTTP call. The first fatal it prints is usually an out-of-memory hit against the wrong CLI php.ini. Processed 0 archives is the opposite case, not a crash but stale data, so confirm reports really are behind, then check the archive throttle, browser archiving, and the site's reporting timezone before you invalidate the stale dates and re-run.
This is the troubleshooting post. If you haven't wired up the cron job yet, start with setting up Matomo cron archiving and come back here when a run misbehaves. Everything below is for Matomo 5.x.
What "the response was empty" actually means
core:archive doesn't build your reports inside the cron process itself. It launches separate PHP workers to compute each archive: CLI subprocesses, and in some configurations HTTP calls back to your own Matomo URL. When one of those workers dies partway through, the parent process gets nothing back. The worker might have run out of memory, hit a fatal PHP error, lacked an extension, failed an SSL handshake, or thrown a database error while writing the archive. The parent can't tell which. All it can report is what it got: an invalid response, the response was empty.
core:archive (cron parent)
CliMulti dispatches one worker per archive
CLI worker
CLI subprocess
climulti:request
worker exits earlyFatal lands in the PHP CLI error log
HTTP worker
HTTP / curl call
back to your own Matomo URL
empty 502 / SSL / DNS / proxyTrace lands in the web server log + matomo.log
CoreAdminHome.archiveReports computes the archive
archive_* tables updated
The message points at memory_limit because that's the most common killer, but it's really a generic "the worker I spawned returned nothing." Where the real error landed depends on how that worker ran. If it was a CLI subprocess, the fatal is in the PHP CLI error log, written by the process that died before it could report anything upstream. If your archiver reaches Matomo over HTTP instead of running the API in-process, look on the web side: the web server error log, matomo.log, and the usual HTTP suspects (an SSL or CA validation failure, DNS, a proxy or firewall in the way). For that case, test whether archiving works against localhost or the direct IP to isolate the network layer from Matomo itself.
One variant to rule out first. If the archiver goes over HTTP and you sit Matomo behind nginx or Apache as a proxy, an empty 502 from the proxy looks exactly like a dead worker. Check why your reverse-proxied Matomo returns 502 before you go chasing PHP limits.
Find the real error before you change anything
Don't start raising limits on a hunch. Get the actual fatal first.
- Find the CLI error log. Run this as the same user that owns the cron job, because the CLI environment is what matters here, not the web one:
php -i | grep error_log- Run the archiver by hand with full verbosity. This surfaces the underlying failure directly instead of through the "empty response" wrapper:
/usr/bin/php /path/to/matomo/console core:archive \
--matomo-domain=https://analytics.example.com -vvvOne flag worth getting right: --matomo-domain is the newer option and points the archiver at the domain Matomo runs on. --url is still valid and is the one you want when Matomo lives under a sub-path (--url=https://example.com/analytics/) or when the archiver has to reach it over plain HTTP at a specific address. Neither is deprecated, so pick whichever matches your install. Then read the first fatal the -vvv run prints. Nine times out of ten, that line is the whole story.
The memory trap: CLI php.ini is not your web php.ini
If the CLI log shows Allowed memory size of N bytes exhausted, you need more memory for the command-line PHP build. Two traps catch almost everyone.
The web server (php-fpm) and the command line use different php.ini files. A generous memory_limit you set in the hosting panel applies to php-fpm, not to cron. That's the whole reason archiving works in the browser and dies on the schedule. Check what CLI actually sees, again as the cron user:
php -i | grep memory_limitAnd watch the units. Use 512M, not 512MB. The B suffix isn't a valid PHP shorthand multiplier, so modern PHP warns about it and either reads the leading number as raw bytes or keeps the previous limit, depending on where it's set. Either way you don't get the 512 MB you thought you asked for, and the value is easy to mistype and never notice. Confirm the real number with php -i | grep memory_limit rather than trusting what's in the file.
You can pin the limit per run instead of editing ini files, which is handy for a quick test:
php -d memory_limit=-1 /path/to/matomo/console core:archive \
--matomo-domain=https://analytics.example.comWhile you're in there, confirm the CLI build is otherwise sane. php -m should list the same extensions as your web server, and date.timezone should be set in the CLI php.ini. A missing timezone or extension throws the same empty-response symptom on cron-only runs.
Raising memory is the right fix for a typo or a default that was just too low. It's the wrong fix as a permanent answer on a large instance that genuinely outgrew its ceiling. That's a capacity question, and we've written separately on how archiving memory and CPU scale. And if the CLI log points at the database rather than memory (a lock wait timeout, max_allowed_packet, a crashed table), the worker is dying for a different reason. See fixing Matomo database errors.
When it runs clean but "Processed 0 archives"
This one isn't a crash. No dead worker, no error to find. Matomo just decided there was nothing to re-archive. That's the right call when your reports are current, and a problem when they're stale. So before you debug anything, confirm it's actually stale: on Matomo 5 you can run ./console diagnostic:archiving-config to dump the relevant settings in one place and ./console diagnostic:archiving-queue to see whether anything is waiting to be processed. If the queue is empty and the config looks deliberate, "Processed 0 archives" is Matomo working as intended. If reports really are behind, a few things put you in the stale-but-silent state.
Browser archiving is the first setting to normalize, not a smoking gun on its own. Leaving "Archive reports when viewed from the browser" on means viewing a report can trigger its own archiving, which muddies what cron is responsible for; it doesn't by itself freeze a dashboard. Still, you want cron to own archiving outright. In Administration → System → General Settings, set it to No. The config equivalent is [General] enable_browser_archiving_triggering = 0. If you run reports on custom segments, also add browser_archiving_disabled_enforce = 1, which keeps segment requests from quietly triggering archiving on demand. Date-range reports are governed by a different key, archiving_range_force_on_browser_request; ranges can still archive on a browser request unless you set that to 0, and you should only do that if you accept that range reports then depend entirely on cron.
The freshness throttle is set high. The same screen has "Archive reports at most every X seconds" (config key time_before_today_archive_considered_outdated). High-traffic setups are often told to raise it to 3600. Set high, today's archive counts as valid for that whole window, so a run in between won't refresh recent data. That's by design, and it's why "today" can look frozen for up to an hour.
The site's reporting timezone isn't what you think. Each website in Matomo has its own timezone, and the "today / yesterday" boundary follows that, not the server clock. If a site's configured timezone crosses its day boundary at a different moment than you expect, an archive Matomo already built can still count as valid for the period you're staring at. Check the website's timezone under its settings and compare it to the report day you're expecting, rather than blindly forcing it to match the server, especially on multi-region installs where sites legitimately run on different zones.
You're on an old 4.x release. There were reports around Matomo 4.7.1 / 4.8.0 (matomo-org/matomo #19023) of a previously created, possibly empty archive being treated as usable and skipped, producing exactly this "Processed 0 archives, no error" with frozen reports. If you're still on that vintage, upgrade to current 5.x before you spend time debugging old 4.x archiving behavior. On 5.x, the cause is configuration or timezone, not that bug.
To force stale dates through, invalidate them and re-archive:
./console core:invalidate-report-data \
--dates=2026-06-01,2026-06-03 --sites=1 --periods=day
./console core:archive --matomo-domain=https://analytics.example.comInvalidating days automatically marks the containing weeks, months, and years stale too, so the day's reprocessing flows up into them on the next archive run. The --cascade flag works the other direction: reach for it when you invalidate a week, month, or year and also want every lower period inside it rebuilt. A blunter option, when you don't feel like enumerating dates, is ./console core:archive --force-all-websites, which reprocesses everything with new visits.
What we'd actually do
Stop reading Matomo's own log first. For the empty-response case, find the log for the path that failed: run core:archive --matomo-domain=... -vvv by hand, and if the dead worker was a CLI subprocess read the CLI error log (php -i | grep error_log as the cron user), or if it went over HTTP read the web server log and matomo.log and check SSL, DNS, and any proxy. The first fatal it prints is almost always the answer: an out-of-memory hit against the wrong php.ini, a 512MB typo, a missing extension, or a database error. Fix that one line and the empty-response runs stop.
For the "Processed 0 archives" case, treat it as configuration, not a crash. Confirm it's genuinely stale with diagnostic:archiving-config and diagnostic:archiving-queue, disable browser archiving so cron owns archiving outright, check the site's reporting timezone, and if you're on an old 4.x, upgrade before you debug anything else. Then invalidate the stale dates and re-run.
Either way, make the next failure easy to read instead of a mystery. Run the job hourly, unless a single run starts approaching or exceeding the hour, in which case lengthen the interval or split low- and high-traffic sites across separate schedules. Send both streams to a log (>> /var/log/matomo-archive.log 2>&1), and keep an eye on Administration → System → Diagnostics for "Last Successful Archiving Completion." That should read in minutes, not days. The moment it starts drifting, you catch the failure on the next run instead of when a client asks why the dashboard hasn't moved since Tuesday.
Martez connects Matomo with Meta Ads and Google Ads so ROAS, CLV, and attribution sit next to your web analytics instead of in a separate spreadsheet. It's in private beta. Join the waitlist if that's relevant to you.
The empty response is never the root cause. It's the symptom that a worker, or an HTTP request back to Matomo, returned nothing the parent could parse. Work out which path failed, then go read the log for that path.