Repository navigation
Benefit cap: model caps about 1.3x DWP's count in 2026-27 (204k vs 160k in May 2026) #1818
Description
Update, 24 Oct 2026: retitled. The 2x gap in the original title ("Benefit cap applies to 2x too many households (5.3% of UC vs 1.6% administrative)") was measured on the Enhanced FRS 2023-24. On Microcosm UK 2024-25 the model matches DWP in 2025-26: 111k households capped, against 115k in February 2026, but this year is calibrated. In 2026-27, the first year out of sample, it caps 204k against DWP's 160k in May 2026. See the 23 Oct comment. #1820, #2076 and #2399 have merged. What remains is the 2026-27 overshoot and the capped group having too few families with children (74% against 81%).
Summary
The model caps 222k UC households (5.30% of its UC caseload). DWP's administrative count is 111k (1.60%) at November 2025. That is 2.0x the count and 3.3x the rate, and it is broad-based rather than concentrated.
Found while trying to reproduce JRF's protected minimum floor estimate with the deductions module from #1815. It is a pre-existing benefit cap problem, not a deductions one, but it dominates that reform: 99% of the floor's modelled cost comes from benefit-capped households.
Enhanced FRS 2023/24 v1.40.3, simulated 2026.
Evidence
| model | DWP admin (Nov 2025) | |
|---|---|---|
| UC households capped | 222k | 111k |
| share of UC households | 5.30% | 1.60% |
| London capped rate | 9.62% | 3.30% |
| non-London capped rate | 4.51% | — |
| capped households with children | 68.4% | 80% |
Two things stand out. The over-capping is roughly 3x in London and outside it, so it is not a regional artefact. And our capped population is too childless — 31.6% without children against 20% in the admin data — which points at single adults, who face the lower cap (GBP 14,753 outside London) and are therefore easiest to tip over.
The excess over the cap is often marginal: p10 is GBP 82/yr and the median GBP 2,733/yr, so a systematic overstatement of capped benefit amounts would push many households over the line.
Ruled out
- Cap levels are correct. GBP 22,020 / 25,323 / 14,753 / 16,967, frozen since April 2023 and unchanged for 2026/27, verified against DWP benefit and pension rates 2026/27.
- Missing exemptions explain under 10% of the excess. Measured directly against the capped population.
- The earnings test's net basis is right. gov.uk specifies earnings after tax and National Insurance, which is what the code does.
- The UC childcare costs element is not an exemption (it is not on the gov.uk list), so its absence is correct.
Defects found along the way
These are real but individually small; none closes the gap.
- Armed Forces Independence Payment is a statutory exemption and is not applied. The variable
armed_forces_independence_paymentexists (variables/gov/dwp/afip.py) but is never referenced byis_benefit_cap_exempt_health_disability, which checksafcsonly. - Exempting benefits with no model variable at all: Guardian's Allowance, Adult Disability Payment, Child Disability Payment, Scottish Adult DLA, Pension Age Disability Payment, war pensions and War Widow's/Widower's Pension. The Scottish disability payments matter structurally, since Scotland has migrated PIP/DLA caseloads onto ADP/CDP — those households are currently capped when they should be exempt.
- The 9-month grace period is not modelled. 84% of capped units have zero earnings, so this bounds the grace-period effect at roughly 15% of the excess, but it is a real omission and an annual model handles it badly.
- The earnings threshold is hardcoded and stale:
earnings_threshold = 10_152(GBP 846/month) inis_benefit_cap_exempt_earnings, against GBP 881/month on gov.uk today. It should be a parameter with dated values. Note the sign: correcting it upward increases capping, so it is not a candidate cause of this bug. - Dead code.
is_benefit_cap_exempt_earningsandis_benefit_cap_exempt_othereach compute ~40 lines of qualifying-benefit variables that are then discarded — copy-paste fromis_benefit_cap_exempt_health_disability, whose return value is the only one that uses them. Both files return a single unrelated expression.
Leading hypotheses for the remaining gap
- Capped benefit amounts overstated, most likely housing. The cap binds on
universal_credit_pre_benefit_cap+housing_benefit_pre_benefit_cap; if modelled housing entitlement runs high, households cross the cap that would not in reality. The marginal excess distribution is consistent with this. - UC caseload composition. The model has 4.22m UC benefit units against ~7.2m administrative (UC caseload runs ~40% below administrative counts, capping aggregate reform estimates policyengine-uk-data#452). If the ~3m missing households are disproportionately low-entitlement in-work claimants, the capped share inflates mechanically. This explains the rate gap but not the 2x absolute count.
- Annualisation. The cap is a monthly test; applying it to annual totals mis-scores households with variable income within the year.
Why it matters
For JRF-style protected floor analysis (#1814), the floor limits combined deductions and benefit cap reductions, so this error passes through almost undamped:
| PolicyEngine UK | JRF (IPPR TBM v02_78) | |
|---|---|---|
| cost | GBP 899m | GBP 340m |
| families gaining | 257k | ~90k |
| mean gain | GBP 67/wk | ~GBP 75/wk |
The mean per-family gain lands close, which is what you would expect if the per-household mechanics are right and the population is wrong. Until this is fixed, PolicyEngine UK cannot be used for benefit-cap-interacting reform estimates.
Activity
vahid-ahmadi commentedon Aug 12, 2026
Traced: overstated rents account for essentially all of the over-capping
Scaling the UC housing costs element down reproduces the administrative count almost exactly:
DWP administrative: 111k. A 25% reduction lands on 114k.
That figure is not arbitrary — it is independently the amount by which modelled rents exceed published averages.
Modelled rents vs published averages
Social rents run ~25–35% high, concentrated in housing associations, whose GBP 181/wk exceeds even the London social average of GBP 140.70. Private rents are ~15% high. Blending in affordable-rent tenancies (up to 80% of market) would raise the social benchmark somewhat, but not to GBP 166.
Housing is 46% of capped benefits and the median excess over the cap is only GBP 2,733/yr against a mean housing element of GBP 11,604/yr, so a rent error of this size moves a large share of households across the threshold. The two magnitudes agreeing is strong circumstantial evidence, though not proof — it should be confirmed in the data pipeline.
This points at rent imputation/uprating in policyengine-uk-data rather than at the benefit cap logic in this repo. The cap code appears to be behaving correctly on overstated inputs. Suggest a companion issue there; the fix is to the rents, not to the housing element, since scaling the element would corrupt UC entitlement generally.
Correction to the exemption analysis above
My earlier under-occupancy test was invalid.
num_bedroomsis identically zero for every household in the dataset, so the "0 spare bedrooms for 100% of capped social renters" result was an artefact of an empty input, not a finding. Two consequences:num_bedroomsis a dead variable — declared invariables/household/demographic/num_bedrooms.pywith no formula and no data behind it, and referenced nowhere else in the model. Anything built on it silently gets zero.Whether under-occupancy matters here is now an open question rather than a closed one, but it is second-order next to the rent overstatement.
vahid-ahmadi commentedon Aug 13, 2026
Corrections, and a finding that reframes this issue
An independent audit of this issue and its analysis found errors in what I posted. Correcting them, most important first.
The quantity in question is already a calibration target, and it is being missed
policyengine-uk-dataalready targets this exact thing (targets/sources/dwp.py:106-127, wired intobuild_loss_matrix.py:352-355):dwp/benefit_capped_householdsdwp/benefit_cap_total_reductionThis reframes the issue. The question is not primarily "is the benefit cap logic or its inputs wrong" but "why is an existing, directly-binding calibration target on this quantity not converging". The same failure appears on
housing/rent_social(1.55x, PolicyEngine/policyengine-uk-data#454) whilehousing/rent_privateconverges at 1.04x — a shared cause is plausible, and a plausible mechanism is loss weighting that lets a GBP 321m target be swamped by GBP 89bn ones. Under investigation.The rent analysis in my 12 August comment is retracted
That comment claimed social rents run 25-35% high and that scaling the housing element by −25% reproduces the administrative count (114k vs 111k). Both are wrong. The benchmark used (RSH "general needs" social rent, GBP 113.69/wk) excludes affordable-rent tenancies, and the counterfactual cut private rents which are not overstated. Corrected in PolicyEngine/policyengine-uk-data#454: the gap is housing association +20%, council +7%, private −5%, and the tenure-correct counterfactual gives 145k, closing about 69% of the excess rather than all of it.
My grace-period bound was backwards
I wrote that "84% of capped units have zero earnings, so this bounds the grace-period effect at roughly 15%". That is the wrong way round: the 9-month grace period applies precisely to households whose earnings have stopped, so zero-earnings is where that population lives, not an upper bound on it. The bound is withdrawn and the grace period is unquantified.
The earnings test belongs in "defects", not "ruled out"
I wrote that the net-earnings basis "is what the code does". Not quite.
is_benefit_cap_exempt_earnings.pycomputessum(employment_income + self_employment_income - income_tax - national_insurance),and
income_taxis total liability across all sources (property, dividends, pension). Tax on unearned income is therefore deducted from earnings, understating them, wrongly removing exemptions and increasing capping — the same direction as this bug. The statutory test is also net of relievable pension contributions, which the code omits.Also, the current threshold is GBP 881/month (2026/27); the hardcoded
10_152is the 2025/26 value (GBP 846/month).Smaller corrections
armed_forces_independence_paymentis genuinely absent from the exemption logic, but the variable has no formula and no dataset input, so applying it will very likely change nothing numerically. My phrasing implied otherwise.is_benefit_cap_exempt_other.pyreturns a three-term expression (has_pensioner | afcs | esa_support_component), not "a single unrelated expression" as I wrote. Roughly 28 dead lines there, ~45 in_earnings. Supporting evidence for the copy-paste diagnosis: the health/disability file includescarer_support_paymentwhile both copies do not.Fixes in flight
The exemption defects above are being fixed separately. The calibration convergence question is the one that matters for the 2x, and it sits in
policyengine-uk-data.vahid-ahmadi commentedon Aug 13, 2026
The 3.1x on total reduction has a distinct, mechanical cause
benefit_cap_reductionmeasures a different quantity from the DWP statistic it is calibrated against.variables/gov/dwp/benefit_cap_reduction.py:That is the gross excess over the cap, unbounded. DWP publishes the amount actually removed, which is bounded by the UC/HB award in payment — Child Benefit, Child Tax Credit and contributory ESA/JSA all count towards the cap but cannot themselves be reduced. Where the excess exceeds the UC award, the model reports the full excess while DWP reports only what could be taken.
universal_credit.pyalready clips the award at zero but still reports the unclipped reduction.This inflates the amount without inflating the count, which is exactly the observed pattern: 3.1x on
dwp/benefit_cap_total_reductionagainst 2.0x ondwp/benefit_capped_households. The implied mean reduction is GBP 86/week modelled against GBP 55.8/week in the target (GBP 320.9m / 110,637 / 52) — 1.55x, and 2.0 x 1.55 = 3.1.Suggested fix: add a
benefit_cap_reduction_applied = min(benefit_cap_reduction, universal_credit_pre_benefit_cap + housing_benefit_pre_benefit_cap)and point the calibration target at that. It is the like-for-like match to the published statistic. Note #1819 introducesuc_benefit_cap_reductionfor the protected-floor case, which is related but not the same thing — that one is about the floor, this one about the award ceiling.And the 2.0x on the count is probably upstream
Separately,
policyengine-uk-dataassigns UC claim status with a take-up rate applied to all benefit units rather than entitled ones (utils/takeup.py:52, rate 0.55), capping the modelled caseload at 4.22m against the repo's own target of 6.096m — details in PolicyEngine/policyengine-uk-data#452.To deliver the OBR's GBP 79.29bn of UC spend from 4.22m units, the average award must run at roughly 1.44x the target-implied level. Benefit cap incidence is convex in the award, so a uniform inflation of that size pushes a disproportionate share over a fixed cap threshold. Modelled cap incidence is 222,000/4.22m = 5.3% of the caseload against DWP's 110,637/6.096m = 1.8%.
That is an inference rather than a measurement, and it is checkable in one step from the calibration log CI already publishes — see PolicyEngine/policyengine-uk-data#456.
Revised reading of this issue
Three distinct causes, in descending order of confidence:
The exemption defects listed in the issue body remain real but are second-order against these.
vahid-ahmadi commentedon Aug 13, 2026
@MaxGhenis orienting summary, since this investigation has spread across two repos and four issues.
Where it started: trying to replicate JRF's protected minimum floor with the deductions module from #1815. The deductions side replicates well; the benefit cap side does not, and that is what this issue became.
What the evidence now says, in descending order of confidence:
benefit_cap_reductionmeasures gross excess, DWP measures amount appliedutils/takeup.py:52; caps caseload at 4.22m against the repo's own 6.096m target; award must then run 1.44x, and cap incidence is convex in the awardsre(1.55x)numerically indistinguishable fromsre(0.645x), so the optimiser is indifferent between the miss and its own fixOpen PRs: #1819 (deductions/floor reconciliation bugs from #1815), #1820 (the exemption defects in this issue), PolicyEngine/policyengine-uk-data#455 (target vintage). All green including the gated microsimulation job. None fixes the 2x — the exemption defects are second-order against the causes above.
Cheapest next step by a wide margin:
constituency_calibration_log.csvis already uploaded as a CI artifact on every push (.github/workflows/push.yaml:63-73) and contains per-target estimate/target/error for the final epoch. Filtering it toname == "UK"would settle most of the open questions across #452, #456, #457 and this issue with no rebuild — in particular whether the OBR UC spend targets are actually being hit, which is the load-bearing assumption in the take-up chain above.Corrections on the record: several claims in my earlier comments here were wrong and are retracted above — the 25-35% rent figure, the -25% counterfactual reproducing the admin count, the grace-period bound (backwards), and "the earnings test is ruled out" (it is a defect, fixed in #1820). Flagging so nobody builds on the retracted versions.
Fix benefit cap exemption defects (#1818)
Drop the strict xfail on the dividend benefit cap case now that #1818'…
Fix benefit cap exemption defects (#1818)
Drop the strict xfail on the dividend benefit cap case now that #1818'…
vahid-ahmadi commentedon Oct 23, 2026
Re-measured on current data (23 October 2026): the 2x gap is gone in the calibrated year, a smaller overshoot shows after April 2026
Microcosm UK 2024-25 national release
a133f37a(sha256 checked against the release manifest), policyengine-uk 2.147.2. Counts are benefit units with a benefit cap reduction above zero, UK-wide. Model year 2025 uses 2025-26 rates, 2026 uses 2026-27 rates. DWP figures are Great Britain; removing Northern Ireland lowers the model counts by under 1k.Model total reduction: £316m a year in 2025, £749m in 2026.
What this says:
Caveats: DWP's May figure is a point-in-time count one month after the April changes, while the model is an annual average over 2026-27, so part of the gap may be timing. The model's UC denominator includes all UC benefit units, DWP's share is of working-age UC households.
Since August, #2076 (cap only the family's benefits, reg 75F/83 exemptions), #2399 (statutory-pay earnings exemption) and #1820 (exemption tests and references) have merged. The figures and causes in the issue body and my August comments were measured on the Enhanced FRS 2023-24, which this release replaces, so treat them as historical.
Suggested next step: a 2026 benefit cap target (May 2026 count and amount bands) in Microcosm, and a check of which April 2026 change drives the extra 44k, starting with the two-child limit removal.
Sources: DWP, Benefit cap: number of households capped to May 2026 (22 September 2026); DWP, Benefit cap: number of households capped to February 2026.
🤖 Generated with Claude Code
[-]Benefit cap applies to 2x too many households (5.3% of UC vs 1.6% administrative)[/-][+]Benefit cap: model caps about 1.3x DWP's count in 2026-27 (204k vs 160k in May 2026)[/+]