Skip to content

[2.x] [messages] fix: refresh DialogMessage after create to resolve number Expression#4384

Merged
imorland merged 1 commit into2.xfrom
fix/messages-dialog-message-number-expression
Feb 24, 2026
Merged

[2.x] [messages] fix: refresh DialogMessage after create to resolve number Expression#4384
imorland merged 1 commit into2.xfrom
fix/messages-dialog-message-number-expression

Conversation

@imorland
Copy link
Copy Markdown
Member

Problem

Every time a user sends a private message, the following error is logged:

ErrorException: Object of class Illuminate\Database\Query\Expression could not be converted to int
in vendor/illuminate/database/Eloquent/Concerns/HasAttributes.php

Root Cause

DialogMessage::creating() sets $message->number to a raw DB Expression object — a subquery that computes COALESCE(MAX(number), 0) + 1 — which Eloquent embeds directly in the INSERT statement for atomic, race-condition-safe message numbering within a dialog. This mirrors the same pattern used in Post::creating().

The problem is that Post has a corresponding Post::created() Eloquent boot hook that calls $post->refresh() to replace the Expression with the real integer value from the DB. DialogMessage has no such hook.

When the API's Create endpoint serializes the newly-created DialogMessage in the response, $model->number is still the Expression object. Eloquent's integer cast then attempts (int) $expression, which:

  • On PHP 8.3: produces a warning (coerced silently to 0, so the response returns "number": 0)
  • On production: Sentry's error handler promotes PHP warnings to ErrorException, causing every message send to log an error

Fix

Call $model->refresh() at the top of DialogMessageResource::created(). This re-queries the database on the write PDO connection and replaces the Expression with the actual integer before any further processing or serialization.

Test

Added test_created_message_response_contains_integer_number_not_expression to CreateTest. Before the fix, this test triggers the PHP warning (confirming the bug). After the fix, all 3 tests pass cleanly with no warnings.

Before fix:
  1 test triggered 1 PHP warning:
  Object of class Illuminate\Database\Query\Expression could not be converted to int

After fix:
  OK (3 tests, 14 assertions)

…Expression

DialogMessage::creating() sets $message->number to a raw DB Expression
object — a subquery that computes COALESCE(MAX(number), 0) + 1 — which
Eloquent embeds directly into the INSERT statement for atomic,
race-condition-safe message numbering.

Unlike Post::created (which calls $post->refresh() in its Eloquent boot
hook), DialogMessage had no equivalent cleanup. When the API's Create
endpoint serialized the newly-created DialogMessage in the response,
$model->number was still the Expression object. Eloquent's integer cast
then attempted (int) $expression, producing:

  ErrorException: Object of class
  Illuminate\Database\Query\Expression could not be converted to int

On PHP 8.3 this is a warning (coerced to 0); on the production server
Sentry's error handler promotes PHP warnings to ErrorExceptions, which
is why the error appears in the log on every message send.

Fix: call $model->refresh() at the top of DialogMessageResource::created()
to re-query the DB and replace the Expression with the real integer before
any further processing or serialization occurs.

Regression test added to CreateTest to assert the response number
attribute is a concrete integer, not a coerced zero from a failed cast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@imorland imorland requested a review from a team as a code owner February 24, 2026 08:12
@imorland imorland added this to the 2.0.0-beta.8 milestone Feb 24, 2026
@imorland imorland merged commit 6b3796a into 2.x Feb 24, 2026
35 checks passed
@imorland imorland deleted the fix/messages-dialog-message-number-expression branch February 24, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant