Codeinwp / Codeinwp/woocommerce-product-addon
Cart total recalculation crashes on non-numeric base price
@Alexia-Soare is already working on this.
Since Sep 17, 2026.
- Dominant language
- PHP
- Stars
- 15
- Forks
- 9
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 16
Description
Summary
Cart total recalculation can terminate an AJAX request with TypeError: Unsupported operand types: float + string when a PPOM-configured cart item has a non-numeric base-price value.
Expected behavior: Cart pricing handles an invalid or formatted base-price value without an uncaught PHP type error.
Actual behavior: The total-price calculation combines the addon total with the base-price value and throws a TypeError.
Impact: Affected carts cannot complete the recalculation request; telemetry recorded repeated occurrences on one production site.
Customer context
- Product / area: PPOM cart pricing
- Version: 34.0.8
- Environment: WordPress 7.1; PHP 8.2.31 and 8.3.31
- Integration / third party: Unknown from available evidence; pricing filters and a currency-conversion integration are present in the inspected path.
- Reported error / symptom:
TypeError: Unsupported operand types: float + stringduring an AJAX cart/session total recalculation. - Impact: 347 occurrences across one distinct production site in the telemetry query window.
Reproduction notes
- Use modern PPOM price mode and add a product with PPOM fields to the cart.
- During
woocommerce_before_calculate_totals, cause the base-price path to return a non-numeric string, such as through an applicable price filter or integration. - Trigger cart/session total recalculation through the AJAX flow.
Observed in production: The reported expression throws TypeError: Unsupported operand types: float + string.
Missing: The telemetry payload does not identify the value producer or exact third-party configuration.
Diagnosis
Conclusion
Production telemetry places the uncaught TypeError at Engine::before_calculate_totals() line 149. The inspected expression adds the normalized addon total to $price_info['price'], but the base-price path can return an unnormalized string. This is a confirmed unsafe operand-type path; the specific producer of the non-numeric string on the affected site is unknown.
Where this likely occurs
src/Pricing/Engine.php—PPOM\Pricing\Engine::before_calculate_totals()lines 119-149 computes$total_addon_priceand then adds it to unvalidated$price_info['price']at line 149.src/Pricing/Engine.php—PPOM\Pricing\Engine::price_get_product_base()lines 1135-1136 assigns the currency-conversion result to$base_price; lines 1211-1216 return that value throughppom_price_infowithout a type boundary.src/Hooks/Callbacks.php—Callbacks::convert_price_back()lines 98-123 documentsfloat|stringinput and returns the supplied value unchanged when its currency branch does not run.src/Cart/WooCommerceCartLifecycleHooks.php—WooCommerceCartLifecycleHooks::register()lines 27-40 attaches the wrapper towoocommerce_before_calculate_totalsin modern price mode.inc/prices.php—ppom_before_calculate_totals()lines 11-13 reaches the namespaced method from the reported stack.- The expression was introduced in
a8f17ea580e3f737592b015c80ba5727fabe6735and remains present in the inspectedv34.0.8source. No earlier working version boundary was established.
Engineering notes
The failure is in the free plugin repository, not the bundled Themeisle SDK. Addon row values pass through normalize_price_value() before their accumulator is returned at src/Pricing/Engine.php — Engine::price_get_addon_total() lines 878-906 and Engine::normalize_price_value() lines 941-952, while the base-price value on the failing expression has no equivalent normalization. The direct source of a non-numeric base-price value may be a pricing filter or currency-related integration; that runtime input was not captured by telemetry.
Test coverage status
tests/unit/src/Pricing/test-pricing-engine.php — test_price_get_addon_total_sums_addon_rows_with_quantity() lines 78-95 covers numeric addon strings. tests/unit/src/Pricing/test-engine-gaps.php — test_price_get_product_base_filter_can_override_result() lines 539-564 covers a numeric filter override. tests/unit/test-third-party-price-mutation.php — testAddonPriceSurvivesThirdPartySetPriceDuringTotals() lines 21-70 covers normal numeric cart recalculation. No relevant coverage was found during inspection for before_calculate_totals() receiving a non-numeric base price and avoiding this TypeError.
What to verify or explore next
- Reproduce totals recalculation with a PPOM cart item whose base-price filter result is a formatted or non-numeric string under PHP 8.2 and 8.3.
- Run the relevant PHPUnit pricing and third-party mutation suites in the repository's WordPress test environment.
- Inspect runtime filters and enabled currency/price extensions on an affected environment to identify the source value.
Unknowns / follow-up
- Telemetry does not include the base-price value or active third-party extensions.
- No version comparison demonstrated that the behavior worked in an earlier release, so regression status remains unconfirmed.
Confidence
Confidence: 96/100
Production telemetry records 347 instances of the same uncaught float + string TypeError in version 34.0.8. The reported line performs arithmetic with a base price that the inspected plugin path leaves unnormalized, while addon totals are normalized.
Crash telemetry
| Occurrences | 347 |
| Distinct sites | 1 |
| First seen | 2026-08-29 03:51 UTC |
| Last seen | 2026-08-30 12:48 UTC |
| Crash location | product:src/Pricing/Engine.php:149 |
| Request context | ajax |
| Inside Themeisle SDK | no |
| Product versions | 34.0.8 |
| WP versions | 7.1 |
| PHP versions | 8.2.31, 8.3.31 |
| SDK versions | 3.3.57 |
Source: automated crash report — woocommerce-product-addon, fingerprint 87dd26a28a7adefd36f30ae6b3c61627
Generated by bug-report-triage (ID: bug-report-triage_6a9518fe103319.12500379)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.