mongodb/laravel-mongodb · error · MathException

BSON type cannot be converted to string

Error message

BSON type %s cannot be converted to string

What it means

When casting values to a decimal, fromDecimal -> asDecimal must convert the value to a string first. If the value is a BSON type that is neither Binary nor Stringable (e.g. ObjectId, Decimal128 in older drivers, BSONDoc/PackedArray), the library throws MathException because it cannot represent it numerically.

Solutions

  1. Cast the BSON value to a PHP scalar before assigning, e.g. (string) $value or $value->jsonSerialize()
  2. Extract payload from Binary values with ->getData() before assignment
  3. Use the appropriate cast (e.g. keep Decimal128 handling via the library's BSON casts) instead of 'decimal'
  4. Normalize data at the ETL/import layer so numeric attributes contain strings or numbers

Example fix

// before
$model->amount = $doc->amount; // raw Decimal128/BSON value
// after
$model->amount = (string) $doc->amount; // convert BSON to string first
Defensive patterns

Strategy: type-guard

Validate before calling

if (is_bson($value) && !$value instanceof \MongoDB\BSON\Binary && !$value instanceof \Stringable) {
    $value = (string) json_encode($value);
}

Type guard

function canBeDecimalString(mixed $value): bool {
    return !is_bson($value) || $value instanceof \MongoDB\BSON\Binary || $value instanceof \Stringable;
}

Try / catch

try {
    $amount = $model->amount; // decimal cast
} catch (MathException $e) {
    if (str_starts_with($e->getMessage(), 'BSON type')) {
        $amount = (string) $rawDoc['amount']; // convert BSON manually
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Assigning a raw BSON value (e.g. a MongoDB\BSON\ObjectId or UTCDateTime) to a model attribute declared with the 'decimal' cast, then reading/saving it.

Common situations: Fetching data written by other drivers where numeric fields are stored as Decimal128 but the installed ext-mongodb/driver version returns non-Stringable types; passing raw BSON documents into decimal-cast attributes in seeds or imports.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.


AI-assisted analysis of mongodb/laravel-mongodb@0634653039 (2026-09-15). Data as JSON: /api/errors/719f2a47b8d8fde6. Report an issue: GitHub.

Appendix: source

Thrown at src/Eloquent/DocumentModel.php:330

        return parent::isJsonCastable($key);
    }

    /**
     * @param mixed $value
     *
     * @inheritdoc
     */
    protected function asDecimal($value, $decimals)
    {
        // Convert BSON to string.
        if ($this->isBSON($value)) {
            if ($value instanceof Binary) {
                $value = $value->getData();
            } elseif ($value instanceof Stringable) {
                $value = (string) $value;
            } else {
                throw new MathException('BSON type ' . $value::class . ' cannot be converted to string');
            }
        }

        return parent::asDecimal($value, $decimals);
    }

    public function fromJson($value, $asObject = false)
    {
        if (is_array($value)) {
            return $asObject ? (object) $value : $value;
        }

        return parent::fromJson($value, $asObject);
    }

    /**
     * Change to mongo native for decimal cast.
     *

View on GitHub (pinned to 0634653039)