Skip to content

[Issue] TypeError in Creditmemo\Validation\QuantityValidator caused by min() with string quantity #40302

@sir1ke

Description

@sir1ke

Preconditions and environment

Preconditions

  • Magento 2.4.8-p3 (Community)
  • PHP 8.4.x (tested with PHP 8.4.13 CLI / FPM)
  • Credit memo creation via API where the quantity is provided as a string
  • Strict types enforced in Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty()

Technical Details

Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty() (and related logic) relies on min() while the function signature requires a strict float type. PHP’s min() is sensitive to both argument order and type juggling:

Be careful when passing arguments of different types because min() can produce unpredictable results.
https://www.php.net/manual/en/function.min.php

With PHP 8.4 and strict typing, this is no longer just “unpredictable” logically, it can result in a hard TypeError when one of the values is a string but the parameter type is declared as float.

We also noticed that installing extensions like Blackfire can change how the quantity is parsed (e.g. the same value is now returned as a float instead of a string), which hides the bug because the types happen to align again.

Proposed Solution

Remove or avoid the direct use of min() when the method signature requires strict float arguments.

Ensure all quantities are explicitly cast to float (or properly validated and converted) before passing them to isValidDecimalRefundQty() and/or before calling min().

Alternatively, replace min() usage with explicit numeric comparison logic that operates on well-typed float values, e.g.:

$qty = (float)$qty;
$itemQty = (float)$itemQty;
$validatedQty = $qty <= $itemQty ? $qty : $itemQty;

This keeps the method signature correct and avoids unexpected type juggling from min() with mixed string/float inputs.

Steps to reproduce

  1. Install Magento 2.4.8-p3 on PHP 8.4
  2. Create an order with at least one shippable item
  3. Create a credit memo via API or custom code where the qty value is passed as a string (e.g. "1.0")
  4. Trigger the credit memo creation / refund so that Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty() is executed
  5. Observe the PHP error.

Expected result

  • Credit memo quantity validation handles string/float input safely
  • No TypeError is thrown

The quantity is correctly validated and the credit memo is created or a proper validation error is returned.

Actual result

A TypeError is thrown:

TypeError: Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator::isValidDecimalRefundQty(): Argument #2 ($itemQty) must be of type float, string given, called in vendor/magento/module-sales/Model/Order/Creditmemo/Validation/QuantityValidator.php on line 136 and defined in vendor/magento/module-sales/Model/Order/Creditmemo/Validation/QuantityValidator.php:107

The root cause is that the code uses min() with values that are not guaranteed to be float, even though the method signature for $itemQty is float. When a string quantity is passed in, PHP 8.4’s behavior plus strict typing leads to the TypeError.

Additional information

With certain extensions installed (e.g. Blackfire), the behavior of the quantity parsing changes and the value becomes a float instead of a string, so the error does not occur. This makes the problem harder to spot.

Release note

No response

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Ready for Confirmation

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions