Describe the bug
When max_followed_severity is configured on a blocklist_instance_destinations entry to a value lower than the severity of an incoming block (e.g. silence while pushing suspend blocks), push_blocklist calls check_followed_severity, which in turn calls fetch_instance_follows (fediblockhole/init.py). That function POSTs to the target instance's /api/v1/admin/measures endpoint with only keys and the per-key filter:
data = {
"keys": [key], # key = "instance_follows"
key: {"domain": domain},
}
response = requests.post(url, headers=requests_headers(token), json=data, timeout=REQUEST_TIMEOUT)
Mastodon's admin measures API requires top-level start_at and end_at parameters on every call. Since they're never sent, the target instance returns 400 Bad Request, and fetch_instance_follows raises an unhandled ValueError, crashing the entire sync run — including any blocks that hadn't been pushed yet.
To Reproduce
- Configure a
blocklist_instance_destinations entry with max_followed_severity = 'silence' (or any value lower than suspend).
- Configure a
blocklist_url_sources or blocklist_instance_sources entry that will produce at least one suspend-severity block.
- Run
fediblock-sync.
- Observe a crash on the first block that exceeds
max_followed_severity:
ValueError: Something went wrong: 400: b'{"error":"param is missing or the value is empty or invalid: start_at"}'
Expected behaviour
fetch_instance_follows should include valid start_at/end_at values in the request body (per the Mastodon admin measures API), so the follows check succeeds instead of raising, and the sync should not abort the whole run on a single failed follows-check.
Environment
- OS/Distribution: Linux (Docker,
mirror.gcr.io/library/python)
- Python version: 3.14
- fediblockhole version: 0.4.6 (bug also present in 0.4.5)
- Mastodon version: 4.6.4 (rejects requests missing
start_at)
Additional context
Confirmed by reading fetch_instance_follows and check_followed_severity in both the 0.4.6 and 0.4.5 releases on PyPI — the missing start_at/end_at params are present in both, so this isn't a recent regression, just a latent bug that's only triggered when max_followed_severity causes the follows-check path to actually run. As a workaround, setting max_followed_severity equal to the highest severity in use ('suspend') avoids the code path entirely, at the cost of losing the "don't suspend domains our users still follow" protection.
Describe the bug
When
max_followed_severityis configured on ablocklist_instance_destinationsentry to a value lower than the severity of an incoming block (e.g.silencewhile pushingsuspendblocks),push_blocklistcallscheck_followed_severity, which in turn callsfetch_instance_follows(fediblockhole/init.py). That function POSTs to the target instance's/api/v1/admin/measuresendpoint with onlykeysand the per-key filter:Mastodon's admin measures API requires top-level
start_atandend_atparameters on every call. Since they're never sent, the target instance returns400 Bad Request, andfetch_instance_followsraises an unhandledValueError, crashing the entire sync run — including any blocks that hadn't been pushed yet.To Reproduce
blocklist_instance_destinationsentry withmax_followed_severity = 'silence'(or any value lower thansuspend).blocklist_url_sourcesorblocklist_instance_sourcesentry that will produce at least onesuspend-severity block.fediblock-sync.max_followed_severity:Expected behaviour
fetch_instance_followsshould include validstart_at/end_atvalues in the request body (per the Mastodon admin measures API), so the follows check succeeds instead of raising, and the sync should not abort the whole run on a single failed follows-check.Environment
mirror.gcr.io/library/python)start_at)Additional context
Confirmed by reading
fetch_instance_followsandcheck_followed_severityin both the 0.4.6 and 0.4.5 releases on PyPI — the missingstart_at/end_atparams are present in both, so this isn't a recent regression, just a latent bug that's only triggered whenmax_followed_severitycauses the follows-check path to actually run. As a workaround, settingmax_followed_severityequal to the highest severity in use ('suspend') avoids the code path entirely, at the cost of losing the "don't suspend domains our users still follow" protection.