Skip to content

Children can receive negative Marriage Allowance and income tax in married benefit units #1666

@anth-volk

Description

@anth-volk

Summary

While investigating PolicyEngine/policyengine.py#344, we found that a married-couple-with-children household can assign negative Marriage Allowance to child records, which then creates taxable earned income and income tax for children with no income.

@vahid-ahmadi could you please confirm whether this is in fact an issue, rather than intentional behavior or a misunderstanding of UK tax law?

Observed behavior

For a household with two adults and two children:

from policyengine_uk import Simulation

situation = {
    "people": {
        "person_0": {"age": {"2026": 42}, "employment_income": {"2026": 55000}},
        "person_1": {"age": {"2026": 40}, "employment_income": {"2026": 35000}},
        "person_2": {"age": {"2026": 8}},
        "person_3": {"age": {"2026": 3}},
    },
    "benunits": {
        "benunit_0": {
            "members": ["person_0", "person_1", "person_2", "person_3"]
        }
    },
    "households": {
        "household_0": {
            "members": ["person_0", "person_1", "person_2", "person_3"]
        }
    },
}

sim = Simulation(situation=situation)
for variable in [
    "marital_status",
    "tax_band",
    "meets_marriage_allowance_income_conditions",
    "unused_personal_allowance",
    "partners_unused_personal_allowance",
    "marriage_allowance",
    "earned_taxable_income",
    "income_tax",
]:
    values = sim.calculate(variable, 2026)
    if hasattr(values, "decode_to_str"):
        values = values.decode_to_str()
    print(variable, list(values))

Current output on main / 2.88.13:

marital_status [MARRIED, MARRIED, MARRIED, MARRIED]
tax_band [HIGHER, BASIC, NONE, NONE]
meets_marriage_allowance_income_conditions [False, True, True, True]
unused_personal_allowance [0.0, 0.0, 12570.0, 12570.0]
partners_unused_personal_allowance [0.0, 0.0, -12570.0, -12570.0]
marriage_allowance [0.0, 0.0, -12570.0, -12570.0]
earned_taxable_income [42430.0, 22430.0, 12570.0, 12570.0]
income_tax [9432.0, 4486.0, 2514.0, 2514.0]

This causes each child to receive income_tax == 2514.0, which is exactly 20% of the personal allowance.

Why this appears to happen

The relevant path appears to be:

  1. marital_status maps every person in a married benefit unit to MARRIED, including children.
  2. gov.hmrc.income_tax.allowances.marriage_allowance.eligible_bands includes NONE after the Marriage Allowance eligibility change.
  3. Children have tax band NONE, so meets_marriage_allowance_income_conditions is true for them.
  4. partners_unused_personal_allowance returns benunit.sum(is_adult * unused_personal_allowance) - pa; for children this becomes 0 - 12570 == -12570.
  5. marriage_allowance allows that negative amount through.
  6. earned_taxable_income subtracts marriage_allowance, so subtracting -12570 creates 12570 taxable earned income.
  7. income_tax then taxes that at the basic rate, producing 2514.

Context

This surfaced because PolicyEngine/policyengine.py#344 updates the UK package from policyengine-uk==2.88.0 to 2.88.6, and the UK household snapshot test for a married couple with two children began failing:

person[2].income_tax: expected 0.0, got 2514.0
person[3].income_tax: expected 0.0, got 2514.0
household.household_tax: expected 18982.05, got 24010.05

The same behavior is still live on current policyengine-uk main / 2.88.13. No relevant changes appear between 2.88.6 and HEAD in the Marriage Allowance/marital-status path.

Question

Should children in a married benefit unit ever be considered MARRIED for Marriage Allowance purposes, or eligible transferors under the NONE tax-band rule? If not, the likely fix is to guard Marriage Allowance eligibility and/or marital status so the transferor logic applies only to adults/spouses, and to prevent negative marriage_allowance values from flowing into taxable income.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions