Skip to content

Malformed UTF-8 characters in PaginatedModelsLoader #2789

Description

@awarrenlove

Describe the bug

#2540 added a json_encode call at https://github.com/nuwave/lighthouse/blob/master/src/Execution/ModelsLoader/PaginatedModelsLoader.php#L89 to compare models rather than toJson, which was changed to encoding the raw original values in #2550 to avoid some casted values not being able to json_encode. However, some raw values cannot be passed to json_encode either, like binary UUID representations as used by https://github.com/michaeldyrynda/laravel-model-uuid. The raw value is a string in PHP but it is not valid UTF-8, so json_encode fails with the JSON_ERROR_UTF8 error.

I am happy to assist with a PR here though I don't think this has an obvious solution so I wanted to discuss in an Issue before I went down that path.

Expected behavior/Solution

Comparison should complete without error while still comparing all values as needed.

  • This could be worked around simply by passing the JSON_INVALID_UTF8_IGNORE or JSON_INVALID_UTF8_SUBSTITUTE flags to json_encode, but this likely has poor side effects I'm not currently thinking about by effectively hiding these values from the comparison.
  • Ideally the comparison should correctly compare the full attributes as intended without failing on non-encodable values either pre- or post-cast.

Steps to reproduce

  1. Define a has-many relationship between two models with a column using the Laravel Model UUID library (or similar non-UTF8 strings)
  2. Return the relationship in a GraphQL field using the @hasMany(type: PAGINATOR) directive
  3. Attempt to load the field, see the error during the json_encode call

Output/Logs

Safe\Exceptions\JsonException: Malformed UTF-8 characters, possibly incorrectly encoded in /.../vendor/thecodingmachine/safe/lib/Exceptions/JsonException.php:9
Stack trace:
#0 /.../vendor/thecodingmachine/safe/generated/8.1/json.php(20): Safe\Exceptions\JsonException::createFromPhpError()
#1 /.../vendor/nuwave/lighthouse/src/Execution/ModelsLoader/PaginatedModelsLoader.php(89): Safe\json_encode(Array)

Lighthouse Version

v6.70.1

Activity

  1. awarrenlove commented on Sep 4, 2026

    @awarrenlove
    ContributorAuthor

    Thinking about this more, since the code is now comparing the raw original values, it may be safe to do a direct array comparison since these should be primitive types now. Does that seem reasonable? Or am I missing a more complex possibility?

  2. awarrenlove commented on Sep 24, 2026

    @awarrenlove
    ContributorAuthor

    I ended up submitting a simpler, though potentially hacky, solution which just forces all strings into UTF-8. This would not be acceptable for most string usage since it would actually change the underlying data, but since all we really care about is comparing values temporarily, this seems safe enough for this use case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions