laravel/framework · error · MathException

Unable to cast value to a decimal.

Error message

Unable to cast value to a decimal.

What it means

The 'decimal:N' cast uses brick/math BigDecimal to scale the value. If the stored/assigned value cannot be parsed as a number, BigDecimal throws and Eloquent wraps it as MathException('Unable to cast value to a decimal.').

Source

Thrown at src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:1541

            default => (float) $value,
        };
    }

    /**
     * Return a decimal as string.
     *
     * @param  float|string  $value
     * @param  int  $decimals
     * @return string
     *
     * @throws \Illuminate\Support\Exceptions\MathException
     */
    protected function asDecimal($value, $decimals)
    {
        try {
            return (string) BigDecimal::of((string) $value)->toScale($decimals, RoundingMode::HalfUp);
        } catch (BrickMathException $e) {
            throw new MathException('Unable to cast value to a decimal.', previous: $e);
        }
    }

    /**
     * Return a timestamp as DateTime object with time set to 00:00:00.
     *
     * @param  mixed  $value
     * @return \Illuminate\Support\Carbon
     */
    protected function asDate($value)
    {
        return $this->asDateTime($value)->startOfDay();
    }

    /**
     * Return a timestamp as DateTime object.
     *
     * @param  mixed  $value

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Sanitize input to a plain numeric string before assignment: cast/strip symbols and thousands separators.
  2. Use a normalizer: $value = filter_var($input, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION).
  3. If the column legitimately holds non-numeric data, change the cast to 'string' or 'json'.

Example fix

// before
$model->price = '$1,234.50'; // cast 'decimal:2' -> throws

// after
$model->price = filter_var('$1,234.50', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
Defensive patterns

Strategy: validation

Validate before calling

if (is_string($value) && ! is_numeric($cleaned = preg_replace(['/[^0-9.\-]/'], '', $value))) {
    throw new \InvalidArgumentException('Cannot cast to decimal: ' . $value);
}
$model->price = $cleaned ?? $value;

Type guard

function isDecimalSafe(mixed $v): bool
{
    return is_numeric($v) || (is_string($v) && is_numeric(preg_replace('/[^0-9.\-]/', '', $v)));
}

Try / catch

try {
    $model->price = $value;
} catch (\Illuminate\Support\Exceptions\MathException $e) {
    if (str_contains($e->getMessage(), 'cast value to a decimal')) {
        $model->price = filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Storing a non-numeric string in a decimal-cast column, e.g. $model->price = 'N/A' with 'price' => 'decimal:2'; importing a CSV where a decimal cell contains currency symbols or whitespace; null coerced to string under unusual drivers.

Common situations: Currency/price fields receiving formatted strings ('$1,234.50'); Excel/CSV import with locale-aware decimals; JSON API sending strings with units; mismatched cast precision vs. column type.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/2b1c41e7d954acf6.json. Report an issue: GitHub.