Summary
Triggering the bundled Utah fully-refundable-EITC contrib reform (gov.contrib.states.ut.child_poverty_impact_dashboard.eitc.in_effect = true) on policyengine-us==1.715.2 raises AttributeError: 'NoneType' object has no attribute 'entity' at calculation time. Same shape as #8640 (MO) — Utah carries the same two bugs.
Reproduce
from policyengine_us import Simulation
from policyengine_core.reforms import Reform
reform = Reform.from_dict({
"gov.contrib.states.ut.child_poverty_impact_dashboard.eitc.in_effect": {"2026-01-01": True},
"gov.states.ut.tax.income.credits.earned_income.rate": {"2026-01-01": 0.3},
}, country_id="us")
sim = Simulation(
situation={
"people": {"head": {"age": {2026: 35}, "employment_income": {2026: 40000}}},
"tax_units": {"tu": {"members": ["head"]}},
"households": {"hh": {"members": ["head"], "state_name": {2026: "UT"}}},
},
reform=reform,
)
sim.calculate("household_net_income", period=2026)
Bug 1 — add() receives a parameter path string
policyengine_us/reforms/states/ut/child_poverty_eitc/ut_fully_refundable_eitc_reform.py:49 —
ut_non_refundable_credits.formula calls:
baseline_non_refundable = add(
tax_unit,
period,
"gov.states.ut.tax.income.credits.non_refundable",
)
policyengine_core.commons.formulas.add expects variables: List[str] and iterates it; when handed a string it iterates character-by-character ("g", "o", "v", …) and get_variable("g") returns None, producing AttributeError: 'NoneType' object has no attribute 'entity'.
Fix: resolve the parameter to its list value before handing it to add():
non_refundable_list = parameters(period).gov.states.ut.tax.income.credits.non_refundable
baseline_non_refundable = add(tax_unit, period, non_refundable_list)
Bug 2 — strict-check ValueError on PE-core 3.26.8 – 3.27.0
Before bug 1 surfaces, on policyengine-core 3.26.8 – 3.27.0 you hit:
ValueError: Variable "ut_refundable_credits" mixes computation modes:
formula and adds/subtracts.
The reform redeclares ut_refundable_credits (and ut_non_refundable_credits) with a formula, but the merge with the baseline inherits adds/subtracts. PE-core 3.27.1 papers over this with an inheritance-aware check, but the reform should still clear adds = None (and subtracts = None if applicable) on each redeclared class so it doesn't depend on PE-core leniency.
Bug 3 (suspected) — refundable EITC capped at tax liability
ut_fully_refundable_eitc.formula returns tax_unit("ut_eitc", period), but ut_eitc is the applied (nonrefundable-capped) credit. A zero-liability filer would receive $0 even though the reform is meant to pay the full credit. Suggest using a ut_eitc_potential (or equivalent uncapped variable) — same fix shape as the MO PR (#8642) switching mo_wftc → mo_wftc_potential. Worth verifying whether such a variable exists in PE-US; if not, computing the uncapped EITC inline.
Expected behavior
The UT fully-refundable EITC reform should run without raising, and should pay the full UT EITC as a refundable credit (not capped at UT income tax liability).
Impact
Any caller setting the in_effect flag — including the child-poverty-impact-dashboard — crashes the simulation entirely (Bug 2) or silently produces household_net_income = 0 after the calling code swallows the AttributeError (Bug 1). The same dual-bug profile as MO (#8640).
Environment
policyengine-us==1.715.2
policyengine-core>=3.26.8 (Bug 2); any version (Bug 1)
- Python 3.11
Related
Summary
Triggering the bundled Utah fully-refundable-EITC contrib reform (
gov.contrib.states.ut.child_poverty_impact_dashboard.eitc.in_effect = true) onpolicyengine-us==1.715.2raisesAttributeError: 'NoneType' object has no attribute 'entity'at calculation time. Same shape as #8640 (MO) — Utah carries the same two bugs.Reproduce
Bug 1 —
add()receives a parameter path stringpolicyengine_us/reforms/states/ut/child_poverty_eitc/ut_fully_refundable_eitc_reform.py:49—ut_non_refundable_credits.formulacalls:policyengine_core.commons.formulas.addexpectsvariables: List[str]and iterates it; when handed a string it iterates character-by-character ("g","o","v", …) andget_variable("g")returnsNone, producingAttributeError: 'NoneType' object has no attribute 'entity'.Fix: resolve the parameter to its list value before handing it to
add():Bug 2 — strict-check
ValueErroron PE-core 3.26.8 – 3.27.0Before bug 1 surfaces, on
policyengine-core3.26.8 – 3.27.0 you hit:The reform redeclares
ut_refundable_credits(andut_non_refundable_credits) with aformula, but the merge with the baseline inheritsadds/subtracts. PE-core 3.27.1 papers over this with an inheritance-aware check, but the reform should still clearadds = None(andsubtracts = Noneif applicable) on each redeclared class so it doesn't depend on PE-core leniency.Bug 3 (suspected) — refundable EITC capped at tax liability
ut_fully_refundable_eitc.formulareturnstax_unit("ut_eitc", period), butut_eitcis the applied (nonrefundable-capped) credit. A zero-liability filer would receive$0even though the reform is meant to pay the full credit. Suggest using aut_eitc_potential(or equivalent uncapped variable) — same fix shape as the MO PR (#8642) switchingmo_wftc→mo_wftc_potential. Worth verifying whether such a variable exists in PE-US; if not, computing the uncapped EITC inline.Expected behavior
The UT fully-refundable EITC reform should run without raising, and should pay the full UT EITC as a refundable credit (not capped at UT income tax liability).
Impact
Any caller setting the
in_effectflag — including the child-poverty-impact-dashboard — crashes the simulation entirely (Bug 2) or silently produceshousehold_net_income = 0after the calling code swallows theAttributeError(Bug 1). The same dual-bug profile as MO (#8640).Environment
policyengine-us==1.715.2policyengine-core>=3.26.8(Bug 2); any version (Bug 1)Related
create_mo_refundable_eitc) crashes when triggered #8640 — MO has the same two bugs.