Summary
taxable_unemployment_compensation assigns the entire tax unit's taxable UI to the head and zero to the spouse, regardless of who actually received the UI. The variable's docstring claims this "will not affect overall tax liability," but it does affect state liability in any state whose AGI flows through adjusted_gross_income_person — including KY, AR, DE, IA, MS, MT (combined-return states), and likely others (e.g., WV, possibly MI).
Originating TAXSIM issue: PolicyEngine/policyengine-taxsim#921 (KY $446 vs TaxAct $524 = $78 off). A parallel WV case (PolicyEngine/policyengine-taxsim#920) likely shares this root cause.
Reproduction
Joint couple, KY, 2025. Primary has no income; spouse has $10K wages and $19,124 UI.
from policyengine_us import Simulation
situation = {
"people": {
"you": {"age": {"2025": 75}, "is_tax_unit_head": {"2025": True},
"unemployment_compensation": {"2025": 1.13}},
"your partner": {"age": {"2025": 40}, "is_tax_unit_spouse": {"2025": True},
"employment_income": {"2025": 10000},
"unemployment_compensation": {"2025": 19123.87}},
},
"tax_units": {"tu": {"members": ["you", "your partner"]}},
"households": {"hh": {"members": ["you", "your partner"], "state_fips": {"2025": 21}}},
"marital_units": {"m": {"members": ["you", "your partner"]}},
"families": {"f": {"members": ["you", "your partner"]}},
"spm_units": {"s": {"members": ["you", "your partner"]}},
}
sim = Simulation(situation=situation)
print(sim.calculate("unemployment_compensation", 2025)) # [1.13, 19123.87] ✓
print(sim.calculate("taxable_unemployment_compensation", 2025)) # [19125., 0.] ✗
print(sim.calculate("adjusted_gross_income_person", 2025)) # [19125., 10000.] (sum OK, split wrong)
print(sim.calculate("ky_income_tax", 2025)) # 446.04, should be ~524
Root cause
taxable_unemployment_insurance.py:14-19:
is_tax_unit_head = person("is_tax_unit_head", period)
tax_unit_taxable_uc = person.tax_unit("tax_unit_taxable_unemployment_compensation", period)
return where(is_tax_unit_head, tax_unit_taxable_uc, 0)
This was a deliberate shortcut so taxable UI (a tax-unit-level concept under §85) could be summed into irs_gross_income (person-level) without double counting. The comment "will not affect overall tax liability" holds for federal AGI (tax_unit sum is unchanged) but breaks per-person AGI used by state code:
adjusted_gross_income_person → irs_gross_income → taxable_unemployment_compensation
- States with combined-return optimization (KY's
ky_files_separately, similar in AR/DE/IA/MS/MT) compare per-spouse tax. When all UI is placed on one spouse, the other spouse appears to have AGI it doesn't (here, $8,500), which lets them claim a separate state standard deduction.
For #921: spouse with no real income gets $8,500 of artificial AGI, claims $3,270 KY std deduction it shouldn't have → $3,270 × 4% ≈ $131 KY tax shortfall (modulo family-size credit).
Suggested fix
Allocate tax_unit_taxable_unemployment_compensation across persons proportionally to each person's unemployment_compensation:
def formula(person, period, parameters):
tax_unit_taxable_uc = person.tax_unit("tax_unit_taxable_unemployment_compensation", period)
person_uc = person("unemployment_compensation", period)
tax_unit_uc = person.tax_unit.sum(person_uc)
share = where(tax_unit_uc > 0, person_uc / tax_unit_uc, 0)
return tax_unit_taxable_uc * share
The federal sum is preserved (shares sum to 1) and per-person AGI reflects the actual recipient.
Integration test
- name: taxable_unemployment_compensation preserves per-person allocation
period: 2025
input:
people:
head:
age: 75
is_tax_unit_head: true
unemployment_compensation: 1_000
spouse:
age: 40
is_tax_unit_spouse: true
unemployment_compensation: 19_000
tax_units:
tax_unit:
members: [head, spouse]
households:
household:
members: [head, spouse]
state_code: KY
output:
taxable_unemployment_compensation: [1_000, 19_000]
And a KY-level integration test reproducing #921's expected output (PE = $524, was $446).
Summary
taxable_unemployment_compensationassigns the entire tax unit's taxable UI to the head and zero to the spouse, regardless of who actually received the UI. The variable's docstring claims this "will not affect overall tax liability," but it does affect state liability in any state whose AGI flows throughadjusted_gross_income_person— including KY, AR, DE, IA, MS, MT (combined-return states), and likely others (e.g., WV, possibly MI).Originating TAXSIM issue: PolicyEngine/policyengine-taxsim#921 (KY $446 vs TaxAct $524 = $78 off). A parallel WV case (PolicyEngine/policyengine-taxsim#920) likely shares this root cause.
Reproduction
Joint couple, KY, 2025. Primary has no income; spouse has $10K wages and $19,124 UI.
Root cause
taxable_unemployment_insurance.py:14-19:This was a deliberate shortcut so taxable UI (a tax-unit-level concept under §85) could be summed into
irs_gross_income(person-level) without double counting. The comment "will not affect overall tax liability" holds for federal AGI (tax_unit sum is unchanged) but breaks per-person AGI used by state code:adjusted_gross_income_person→irs_gross_income→taxable_unemployment_compensationky_files_separately, similar in AR/DE/IA/MS/MT) compare per-spouse tax. When all UI is placed on one spouse, the other spouse appears to have AGI it doesn't (here, $8,500), which lets them claim a separate state standard deduction.For #921: spouse with no real income gets $8,500 of artificial AGI, claims $3,270 KY std deduction it shouldn't have → $3,270 × 4% ≈ $131 KY tax shortfall (modulo family-size credit).
Suggested fix
Allocate
tax_unit_taxable_unemployment_compensationacross persons proportionally to each person'sunemployment_compensation:The federal sum is preserved (shares sum to 1) and per-person AGI reflects the actual recipient.
Integration test
And a KY-level integration test reproducing #921's expected output (PE = $524, was $446).