diff --git a/packages/react-relay/__tests__/RelayResolvers-abstractTypeRootFragment-test.js b/packages/react-relay/__tests__/RelayResolvers-abstractTypeRootFragment-test.js
new file mode 100644
index 0000000000000..87a507857be0e
--- /dev/null
+++ b/packages/react-relay/__tests__/RelayResolvers-abstractTypeRootFragment-test.js
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict-local
+ * @format
+ * @oncall relay
+ */
+
+'use strict';
+
+import type {IEnvironment} from 'relay-runtime';
+
+const React = require('react');
+const {RelayEnvironmentProvider, useLazyLoadQuery} = require('react-relay');
+const TestRenderer = require('react-test-renderer');
+const {graphql} = require('relay-runtime/query/GraphQLTag');
+const {createMockEnvironment} = require('relay-test-utils');
+const {
+ disallowConsoleErrors,
+ disallowWarnings,
+ injectPromisePolyfill__DEPRECATED,
+} = require('relay-test-utils-internal');
+
+injectPromisePolyfill__DEPRECATED();
+disallowWarnings();
+disallowConsoleErrors();
+
+function EnvironmentWrapper({
+ children,
+ environment,
+}: {
+ children: React.Node,
+ environment: IEnvironment,
+}) {
+ return (
+
+ {children}
+
+ );
+}
+
+function NodeGreeting() {
+ const data = useLazyLoadQuery(
+ graphql`
+ query RelayResolversAbstractTypeRootFragmentTestQuery {
+ node(id: "4") {
+ node_greeting
+ }
+ }
+ `,
+ {},
+ );
+ return data.node?.node_greeting;
+}
+
+// https://github.com/facebook/relay/issues/4943#issuecomment-3221637018
+test('Can read a resolver with a rootFragment on an abstract type', async () => {
+ const logEvents = [];
+ const environment = createMockEnvironment({
+ relayFieldLogger(event) {
+ logEvents.push(event);
+ },
+ });
+ let renderer;
+ TestRenderer.act(() => {
+ renderer = TestRenderer.create(
+
+
+ ,
+ );
+ });
+ expect(renderer?.toJSON()).toEqual('Loading...');
+ expect(logEvents).toEqual([]);
+
+ await TestRenderer.act(() => {
+ environment.mock.resolveMostRecentOperation({
+ data: {
+ node: {
+ __typename: 'User',
+ __isNode: 'User',
+ id: '4',
+ },
+ },
+ });
+ jest.runAllImmediates();
+ });
+
+ // Incorrect! We provided `__isNode`!
+ expect(logEvents).toEqual([
+ {
+ fieldPath: '',
+ kind: 'missing_expected_data.log',
+ owner: 'NodeResolversGreeting',
+ uiContext: undefined,
+ },
+ ]);
+ // Incorrect! Should be the greeting.
+ expect(renderer?.toJSON()).toEqual(null);
+});
diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversAbstractTypeRootFragmentTestQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversAbstractTypeRootFragmentTestQuery.graphql.js
new file mode 100644
index 0000000000000..326c00bedfac4
--- /dev/null
+++ b/packages/react-relay/__tests__/__generated__/RelayResolversAbstractTypeRootFragmentTestQuery.graphql.js
@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @oncall relay
+ *
+ * @generated SignedSource<<99027ecd32b72e3db29e851998075f28>>
+ * @flow
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { ConcreteRequest, Query } from 'relay-runtime';
+import type { NodeResolversGreeting$key } from "./../../../relay-runtime/store/__tests__/resolvers/__generated__/NodeResolversGreeting.graphql";
+import {node_greeting as nodeNodeGreetingResolverType} from "../../../relay-runtime/store/__tests__/resolvers/NodeResolvers.js";
+import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType";
+// Type assertion validating that `nodeNodeGreetingResolverType` resolver is correctly implemented.
+// A type error here indicates that the type signature of the resolver module is incorrect.
+(nodeNodeGreetingResolverType: (
+ rootKey: NodeResolversGreeting$key,
+ args: void,
+ context: TestResolverContextType,
+) => ?string);
+export type RelayResolversAbstractTypeRootFragmentTestQuery$variables = {||};
+export type RelayResolversAbstractTypeRootFragmentTestQuery$data = {|
+ +node: ?{|
+ +node_greeting: ?string,
+ |},
+|};
+export type RelayResolversAbstractTypeRootFragmentTestQuery = {|
+ response: RelayResolversAbstractTypeRootFragmentTestQuery$data,
+ variables: RelayResolversAbstractTypeRootFragmentTestQuery$variables,
+|};
+*/
+
+var node/*: ConcreteRequest*/ = (function(){
+var v0 = [
+ {
+ "kind": "Literal",
+ "name": "id",
+ "value": "4"
+ }
+],
+v1 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "id",
+ "storageKey": null
+};
+return {
+ "fragment": {
+ "argumentDefinitions": [],
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "RelayResolversAbstractTypeRootFragmentTestQuery",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v0/*: any*/),
+ "concreteType": null,
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "fragment": {
+ "args": null,
+ "kind": "FragmentSpread",
+ "name": "NodeResolversGreeting"
+ },
+ "kind": "RelayResolver",
+ "name": "node_greeting",
+ "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/NodeResolvers').node_greeting,
+ "path": "node.node_greeting"
+ }
+ ],
+ "storageKey": "node(id:\"4\")"
+ }
+ ],
+ "type": "Query",
+ "abstractKey": null
+ },
+ "kind": "Request",
+ "operation": {
+ "argumentDefinitions": [],
+ "kind": "Operation",
+ "name": "RelayResolversAbstractTypeRootFragmentTestQuery",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v0/*: any*/),
+ "concreteType": null,
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+ },
+ {
+ "name": "node_greeting",
+ "args": null,
+ "fragment": {
+ "kind": "InlineFragment",
+ "selections": [
+ (v1/*: any*/)
+ ],
+ "type": "Node",
+ "abstractKey": "__isNode"
+ },
+ "kind": "RelayResolver",
+ "storageKey": null,
+ "isOutputType": true
+ },
+ (v1/*: any*/)
+ ],
+ "storageKey": "node(id:\"4\")"
+ }
+ ]
+ },
+ "params": {
+ "cacheID": "16a426563480b45f425459f7da9a621f",
+ "id": null,
+ "metadata": {},
+ "name": "RelayResolversAbstractTypeRootFragmentTestQuery",
+ "operationKind": "query",
+ "text": "query RelayResolversAbstractTypeRootFragmentTestQuery {\n node(id: \"4\") {\n __typename\n ...NodeResolversGreeting\n id\n }\n}\n\nfragment NodeResolversGreeting on Node {\n __isNode: __typename\n id\n}\n"
+ }
+};
+})();
+
+if (__DEV__) {
+ (node/*: any*/).hash = "38ce80d969e3fdad2e27031ddd534888";
+}
+
+module.exports = ((node/*: any*/)/*: Query<
+ RelayResolversAbstractTypeRootFragmentTestQuery$variables,
+ RelayResolversAbstractTypeRootFragmentTestQuery$data,
+>*/);
diff --git a/packages/relay-runtime/store/__tests__/resolvers/NodeResolvers.js b/packages/relay-runtime/store/__tests__/resolvers/NodeResolvers.js
new file mode 100644
index 0000000000000..4ea07205e544e
--- /dev/null
+++ b/packages/relay-runtime/store/__tests__/resolvers/NodeResolvers.js
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict-local
+ * @format
+ * @oncall relay
+ */
+
+'use strict';
+
+import type {NodeResolversGreeting$key} from './__generated__/NodeResolversGreeting.graphql';
+
+const {graphql} = require('relay-runtime');
+const {readFragment} = require('relay-runtime/store/ResolverFragments');
+
+/**
+ * @RelayResolver Node.node_greeting: String
+ * @rootFragment NodeResolversGreeting
+ */
+function node_greeting(rootKey: NodeResolversGreeting$key): string {
+ const node = readFragment(
+ graphql`
+ fragment NodeResolversGreeting on Node {
+ id
+ }
+ `,
+ rootKey,
+ );
+ return `Hello Node with id ${node.id}!`;
+}
+
+module.exports = {
+ node_greeting,
+};
diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/NodeResolversGreeting.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/NodeResolversGreeting.graphql.js
new file mode 100644
index 0000000000000..2f5988d659606
--- /dev/null
+++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/NodeResolversGreeting.graphql.js
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @oncall relay
+ *
+ * @generated SignedSource<<65d3cfa934e7852b8f03a5fab85b131b>>
+ * @flow
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { Fragment, ReaderFragment } from 'relay-runtime';
+import type { FragmentType } from "relay-runtime";
+declare export opaque type NodeResolversGreeting$fragmentType: FragmentType;
+export type NodeResolversGreeting$data = {|
+ +id: string,
+ +$fragmentType: NodeResolversGreeting$fragmentType,
+|};
+export type NodeResolversGreeting$key = {
+ +$data?: NodeResolversGreeting$data,
+ +$fragmentSpreads: NodeResolversGreeting$fragmentType,
+ ...
+};
+*/
+
+var node/*: ReaderFragment*/ = {
+ "argumentDefinitions": [],
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "NodeResolversGreeting",
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "id",
+ "storageKey": null
+ }
+ ],
+ "type": "Node",
+ "abstractKey": "__isNode"
+};
+
+if (__DEV__) {
+ (node/*: any*/).hash = "d331274a92b58d1de6941541d745b6ae";
+}
+
+module.exports = ((node/*: any*/)/*: Fragment<
+ NodeResolversGreeting$fragmentType,
+ NodeResolversGreeting$data,
+>*/);