Fix variable validation and input coercion for per-schema scalar overrides#1876
Fix variable validation and input coercion for per-schema scalar overrides#1876
Conversation
…rides Per-schema scalar overrides (#1869) allow type loaders to return custom instances for built-in scalars (ID, String, Int, Float, Boolean). This introduced two bugs where the custom instance and the built-in singleton diverge, causing identity checks to fail. Bug 1: Variable validation rejects valid queries. TypeComparators uses === to compare types. When a variable type is resolved via Schema::getType() (returning the custom instance) but the argument type references the built-in singleton, the identity check fails and VariablesInAllowedPosition reports a spurious error: "Variable "$id" of type "ID!" used in position expecting type "ID!"." Fix: Add name-based equality in isEqualType() and isTypeSubTypeOf() for built-in scalars, so two ScalarType instances with the same built-in name are considered equal. Bug 2: Input coercion calls the wrong parseValue(). Value::coerceInputValue() calls parseValue() on the type from field definitions (the built-in singleton), not the custom override registered via the type loader. Custom parsing logic (e.g. decoding a global ID) is silently skipped. Fix: Pass the Schema into coerceInputValue() and resolve built-in scalars from the schema before calling parseValue(), mirroring the output-side fix already in ReferenceExecutor::completeValue(). Additionally: - Add Type::isBuiltInScalar() to centralize the instanceof + name check used across TypeComparators, Value, and ReferenceExecutor. - Scope the ReferenceExecutor schema type resolution (from #1869) to built-in scalars only, matching the narrower fix elsewhere. - Replace silent fallbacks with assertions when the schema returns an unexpected type for a built-in scalar name. Fixes #1874 Ref #1869
There was a problem hiding this comment.
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
Alright, all tests pass on our project. I think this looks better now. It explicitly checks for built in scalar types |
|
Works in Lighthouse. Currently running tests in our large company project to see if that works too. Have you tried any benchmarks? If you use Claude Code by chance, you can use the |
|
@spawnia Please have another look. |
But feel free to try it yourself. |
|
Perfect. Ran well in our large project too. Want to wait on feedback from #1874 (comment), or merge and release immediately? |
|
Given that it's already broken and that we both tested it, I think best to just merge and release. Is that already automated? 😄 |
|
Not quite. I am working on the long list of pending mails from this project, will get to it at some point. Thanks for the fix, I released it as https://github.com/webonyx/graphql-php/releases/tag/v15.31.1. |
|
I've just tested this on my Magento 2 store and it is still erroring. |
|
Please provide more details. An error log line from a production system is not enough. |
Summary
This affects all Magento GraphQL functionality (reviews, cart operations via GraphQL, etc.) on new installs or upgrades, since Root CauseIn v15.30.2 (working): // Built-in scalars checked FIRST
$standardTypes = Type::getStandardTypes();
if (isset($standardTypes[$name])) {
return $standardTypes[$name];
}
// typeLoader only called for non-scalar types
$type = $this->loadType($name);v15.31.1 (broken): // typeLoader called FIRST for ALL types, including scalars
$type = $this->loadType($name);
if ($type !== null) {
return ...;
}
// Built-in scalars only as fallback
$builtInScalars = Type::builtInScalars();
if (isset($builtInScalars[$name])) {
return ...;
}Magento registers a How to Reproduce
Possible Fix: Magento-sideThe proper fix would be in Magento's Temporary Workaround in MagentoIn case someone else from the Magento ecosystem comes across this, pinning the library to |
|
Would be best to spend those AI tokens on creating a PR in Magento to fix it. |
|
Creating a PR isn't the problem - getting it released will take a while :) |
|
This seems to break the I keep the GraphQLite fixed to version 8.0.0 but I didn't have this sub-dependency fixed to a version. After updating, this got updated from v15.30.2 to v15.31.1 and now GraphQL stopped working and all I receive is I use custom class finder based on Also reported here: thecodingmachine/graphqlite#785 |
Already fixed in the implementing library 👍 |
Per-schema scalar overrides (#1869) allow type loaders to return custom instances for built-in scalars (ID, String, Int, Float, Boolean). This introduced two bugs where the custom instance and the built-in singleton diverge, causing identity checks to fail.
Bug 1: Variable validation rejects valid queries. TypeComparators uses === to compare types. When a variable type is resolved via Schema::getType() (returning the custom instance) but the argument type references the built-in singleton, the identity check fails and VariablesInAllowedPosition reports a spurious error: "Variable "$id" of type "ID!" used in position expecting type "ID!"."
Fix: Add name-based equality in isEqualType() and isTypeSubTypeOf() for built-in scalars, so two ScalarType instances with the same built-in name are considered equal.
Bug 2: Input coercion calls the wrong parseValue(). Value::coerceInputValue() calls parseValue() on the type from field definitions (the built-in singleton), not the custom override registered via the type loader. Custom parsing logic (e.g. decoding a global ID) is silently skipped.
Fix: Pass the Schema into coerceInputValue() and resolve built-in scalars from the schema before calling parseValue(), mirroring the output-side fix already in ReferenceExecutor::completeValue().
Additionally:
Fixes #1874
Ref #1869