Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 89 additions & 15 deletions packages/executor/src/execution/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
isNonNullType,
isObjectType,
Kind,
locatedError,
OperationDefinitionNode,
SchemaMetaFieldDef,
TypeMetaFieldDef,
Expand Down Expand Up @@ -58,6 +57,7 @@
import { coerceError } from './coerceError.js';
import { flattenAsyncIterable } from './flattenAsyncIterable.js';
import { invariant } from './invariant.js';
import { locatedError } from './locatedError.js';

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Type Check on GraphQL v15

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Full Check on GraphQL v16

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Unit Test on Bun

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / deployment

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Unit Test on Node 22 (ubuntu-latest) and GraphQL v16

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Unit Test on Node 18 (ubuntu-latest) and GraphQL v16

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Unit Test on Node 18 (ubuntu-latest) and GraphQL v15

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Unit Test on Node 24 (ubuntu-latest) and GraphQL v16

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / Unit Test on Node 18 (windows-latest) and GraphQL v16

Cannot find module './locatedError.js' or its corresponding type declarations.

Check failure on line 60 in packages/executor/src/execution/execute.ts

View workflow job for this annotation

GitHub Actions / alpha / snapshot

Cannot find module './locatedError.js' or its corresponding type declarations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix incorrect import path causing build failures.

The import is trying to load from a non-existent file './locatedError.js'. The locatedError function is actually exported from '@graphql-tools/utils', which is already imported at the top of this file.

Apply this diff:

-import { locatedError } from './locatedError.js';

And add locatedError to the existing import from '@graphql-tools/utils' at line 30-53:

 import {
   collectSubFields as _collectSubfields,
   addPath,
   collectFields,
   createGraphQLError,
   fakePromise,
   getArgumentValues,
   getDefinedRootType,
   GraphQLResolveInfo,
   GraphQLStreamDirective,
   inspect,
   isAsyncIterable,
   isIterableObject,
   isObjectLike,
   isPromise,
+  locatedError,
   mapAsyncIterator,
   Maybe,
   MaybePromise,
   memoize1,
   memoize3,
   Path,
   pathToArray,
   promiseReduce,
 } from '@graphql-tools/utils';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { locatedError } from './locatedError.js';
// Remove this line – it references a non-existent file
// import { locatedError } from './locatedError.js';
import {
collectSubFields as _collectSubfields,
addPath,
collectFields,
createGraphQLError,
fakePromise,
getArgumentValues,
getDefinedRootType,
GraphQLResolveInfo,
GraphQLStreamDirective,
inspect,
isAsyncIterable,
isIterableObject,
isObjectLike,
isPromise,
locatedError, // ← newly imported from @graphql-tools/utils
mapAsyncIterator,
Maybe,
MaybePromise,
memoize1,
memoize3,
Path,
pathToArray,
promiseReduce,
} from '@graphql-tools/utils';
🧰 Tools
🪛 GitHub Actions: pr

[error] 60-60: TS2307: Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Actions: website

[error] 60-60: Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: alpha / snapshot

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: deployment

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: Full Check on GraphQL v16

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: Type Check on GraphQL v15

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: Unit Test on Bun

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: Unit Test on Node 18 (ubuntu-latest) and GraphQL v15

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: Unit Test on Node 18 (ubuntu-latest) and GraphQL v16

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: Unit Test on Node 22 (ubuntu-latest) and GraphQL v16

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🪛 GitHub Check: Unit Test on Node 24 (ubuntu-latest) and GraphQL v16

[failure] 60-60:
Cannot find module './locatedError.js' or its corresponding type declarations.

🤖 Prompt for AI Agents
In packages/executor/src/execution/execute.ts around line 60, the file
incorrectly imports locatedError from './locatedError.js' which doesn't exist;
instead add locatedError to the existing import list from '@graphql-tools/utils'
(the import block at ~lines 30-53) and remove the bogus './locatedError.js'
import line so the symbol is resolved from the correct package.

import { promiseForObject } from './promiseForObject.js';
import { getVariableValues } from './values.js';

Expand Down Expand Up @@ -127,6 +127,7 @@
signal?: AbortSignal;
onSignalAbort?(handler: () => void): void;
signalPromise?: Promise<never>;
schemaCoordinateInErrors?: boolean;
}

export interface FormattedExecutionResult<
Expand Down Expand Up @@ -247,6 +248,7 @@
typeResolver?: Maybe<GraphQLTypeResolver<any, TContext>>;
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
signal?: AbortSignal;
schemaCoordinateInErrors?: boolean;
}

/**
Expand Down Expand Up @@ -419,6 +421,7 @@
typeResolver,
subscribeFieldResolver,
signal,
schemaCoordinateInErrors,
} = args;

signal?.throwIfAborted();
Expand Down Expand Up @@ -531,6 +534,7 @@
signal,
onSignalAbort,
signalPromise,
schemaCoordinateInErrors,
};
}

Expand Down Expand Up @@ -746,14 +750,24 @@
let result: unknown;
for (let rawErrorItem of rawError.errors) {
rawErrorItem = coerceError(rawErrorItem);
const error = locatedError(rawErrorItem, fieldNodes, pathToArray(path));
const error = locatedError(
rawErrorItem,
fieldNodes,
pathToArray(path),
exeContext.schemaCoordinateInErrors && info,
);
result = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
}
return result;
}
rawError = coerceError(rawError);
const error = locatedError(rawError, fieldNodes, pathToArray(path));
const error = locatedError(
rawError,
fieldNodes,
pathToArray(path),
exeContext.schemaCoordinateInErrors && info,
);
const handledError = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
return handledError;
Expand All @@ -765,14 +779,24 @@
let result: unknown;
for (let rawErrorItem of rawError.errors) {
rawErrorItem = coerceError(rawErrorItem);
const error = locatedError(rawErrorItem, fieldNodes, pathToArray(path));
const error = locatedError(
rawErrorItem,
fieldNodes,
pathToArray(path),
exeContext.schemaCoordinateInErrors && info,
);
result = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
}
return result;
}
const coercedError = coerceError(rawError);
const error = locatedError(coercedError, fieldNodes, pathToArray(path));
const error = locatedError(
coercedError,
fieldNodes,
pathToArray(path),
exeContext.schemaCoordinateInErrors && info,
);
const handledError = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
return handledError;
Expand Down Expand Up @@ -1041,7 +1065,12 @@
}
} catch (rawError) {
const coercedError = coerceError(rawError);
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
coercedError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
completedResults.push(handleFieldError(error, itemType, errors));
break;
}
Expand Down Expand Up @@ -1201,7 +1230,12 @@
completedResults.push(
completedItem.then(undefined, rawError => {
rawError = coerceError(rawError);
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
rawError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
const handledError = handleFieldError(error, itemType, errors);
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
return handledError;
Expand All @@ -1214,7 +1248,12 @@
completedResults.push(completedItem);
} catch (rawError) {
const coercedError = coerceError(rawError);
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
coercedError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
const handledError = handleFieldError(error, itemType, errors);
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
completedResults.push(handledError);
Expand Down Expand Up @@ -1789,13 +1828,23 @@
return result
.then(result => assertEventStream(result, exeContext.signal, exeContext.onSignalAbort))
.then(undefined, error => {
throw locatedError(error, fieldNodes, pathToArray(path));
throw locatedError(
error,
fieldNodes,
pathToArray(path),
exeContext.schemaCoordinateInErrors && info,
);
});
}

return assertEventStream(result, exeContext.signal, exeContext.onSignalAbort);
} catch (error) {
throw locatedError(error, fieldNodes, pathToArray(path));
throw locatedError(
error,
fieldNodes,
pathToArray(path),
exeContext.schemaCoordinateInErrors && info,
);
}
}

Expand Down Expand Up @@ -1921,15 +1970,25 @@
// to take a second callback for the error case.
completedItem = completedItem.then(undefined, rawError => {
rawError = coerceError(rawError);
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
rawError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors);
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
return handledError;
});
}
} catch (rawError) {
const coercedError = coerceError(rawError);
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
coercedError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
completedItem = handleFieldError(error, itemType, asyncPayloadRecord.errors);
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
}
Expand Down Expand Up @@ -1977,7 +2036,12 @@
item = value;
} catch (rawError) {
const coercedError = coerceError(rawError);
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
coercedError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors);
// don't continue if iterator throws
return { done: true, value };
Expand All @@ -1996,15 +2060,25 @@

if (isPromise(completedItem)) {
completedItem = completedItem.then(undefined, rawError => {
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
rawError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors);
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
return handledError;
});
}
return { done: false, value: completedItem };
} catch (rawError) {
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
const error = locatedError(
rawError,
fieldNodes,
pathToArray(itemPath),
exeContext.schemaCoordinateInErrors && info,
);
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors);
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
return { done: false, value };
Expand Down
1 change: 1 addition & 0 deletions packages/executor/src/execution/normalizedExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const executorFromSchema = memoize1(function executorFromSchema(
rootValue: request.rootValue,
contextValue: request.context,
signal: request.signal || request.info?.signal,
schemaCoordinateInErrors: request.schemaCoordinateInErrors,
});
};
});
7 changes: 7 additions & 0 deletions packages/utils/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ export interface ExecutionRequest<
subgraphName?: string;
info?: GraphQLResolveInfo;
signal?: AbortSignal;
/**
* Enable/Disable the addition of field schema coordinate in GraphQL Errors extension
*
* Note: Schema Coordinate are exposed using Symbol.for('schemaCoordinate') so that it's not
* serialized. Exposing schema coordinate can ease the discovery of private schemas.
*/
schemaCoordinateInErrors?: boolean;
}

// graphql-js non-exported typings
Expand Down
40 changes: 37 additions & 3 deletions packages/utils/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ASTNode, GraphQLError, Source, versionInfo } from 'graphql';
import { locatedError as _locatedError, ASTNode, GraphQLError, Source, versionInfo } from 'graphql';
import { Maybe } from './types.js';

interface GraphQLErrorOptions {
Expand Down Expand Up @@ -60,16 +60,50 @@
);
}

type SchemaCoordinateInfo = { fieldName: string; parentType: { name: string } };
export const ERROR_EXTENSION_SCHEMA_COORDINATE = Symbol.for('graphql.error.schemaCoordinate');
function addSchemaCoordinateToError(error: GraphQLError, info: SchemaCoordinateInfo): void {
// @ts-expect-error extensions can't be Symbol in official GraphQL Error type
error.extensions[ERROR_EXTENSION_SCHEMA_COORDINATE] = `${info.parentType.name}.${info.fieldName}`;
}

export function locatedError(
rawError: unknown,
nodes: ASTNode | ReadonlyArray<ASTNode> | null | undefined,
path: Maybe<ReadonlyArray<string | number>>,
info: SchemaCoordinateInfo | false | null | undefined,
) {
const error = _locatedError(rawError, nodes, path);

Check failure on line 76 in packages/utils/src/errors.ts

View workflow job for this annotation

GitHub Actions / Type Check on GraphQL v15

Argument of type 'ASTNode | readonly ASTNode[] | null | undefined' is not assignable to parameter of type 'ASTNode | readonly ASTNode[] | undefined'.

Check failure on line 76 in packages/utils/src/errors.ts

View workflow job for this annotation

GitHub Actions / Unit Test on Node 18 (ubuntu-latest) and GraphQL v15

Argument of type 'ASTNode | readonly ASTNode[] | null | undefined' is not assignable to parameter of type 'ASTNode | readonly ASTNode[] | undefined'.

if (info) {
addSchemaCoordinateToError(error, info);
}

return error;
}
Comment on lines +70 to +83
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix type incompatibility with GraphQL's locatedError.

The nodes parameter is typed as ASTNode | ReadonlyArray<ASTNode> | null | undefined, but GraphQL's locatedError function only accepts undefined (not null) according to the static analysis hints. This is causing build failures.

Apply this diff to fix the type:

 export function locatedError(
   rawError: unknown,
-  nodes: ASTNode | ReadonlyArray<ASTNode> | null | undefined,
+  nodes: ASTNode | ReadonlyArray<ASTNode> | undefined,
   path: Maybe<ReadonlyArray<string | number>>,
   info: SchemaCoordinateInfo | false | null | undefined,
 ) {
   const error = _locatedError(rawError, nodes, path);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function locatedError(
rawError: unknown,
nodes: ASTNode | ReadonlyArray<ASTNode> | null | undefined,
path: Maybe<ReadonlyArray<string | number>>,
info: SchemaCoordinateInfo | false | null | undefined,
) {
const error = _locatedError(rawError, nodes, path);
if (info) {
addSchemaCoordinateToError(error, info);
}
return error;
}
export function locatedError(
rawError: unknown,
nodes: ASTNode | ReadonlyArray<ASTNode> | undefined,
path: Maybe<ReadonlyArray<string | number>>,
info: SchemaCoordinateInfo | false | null | undefined,
) {
const error = _locatedError(rawError, nodes, path);
if (info) {
addSchemaCoordinateToError(error, info);
}
return error;
}
🧰 Tools
🪛 GitHub Check: Type Check on GraphQL v15

[failure] 76-76:
Argument of type 'ASTNode | readonly ASTNode[] | null | undefined' is not assignable to parameter of type 'ASTNode | readonly ASTNode[] | undefined'.

🪛 GitHub Check: Unit Test on Node 18 (ubuntu-latest) and GraphQL v15

[failure] 76-76:
Argument of type 'ASTNode | readonly ASTNode[] | null | undefined' is not assignable to parameter of type 'ASTNode | readonly ASTNode[] | undefined'.

🤖 Prompt for AI Agents
In packages/utils/src/errors.ts around lines 70 to 83, change the nodes
parameter type to remove null so it matches GraphQL's locatedError signature
(ASTNode | ReadonlyArray<ASTNode> | undefined), and when calling _locatedError
ensure you never pass null by normalizing nodes to undefined (e.g., treat null
as undefined) before the call; keep adding schema coordinate to the returned
error as-is.


export function relocatedError(
originalError: GraphQLError,
path?: ReadonlyArray<string | number>,
info?: SchemaCoordinateInfo | false | null | undefined,
): GraphQLError {
return createGraphQLError(originalError.message, {
const error = createGraphQLError(originalError.message, {
nodes: originalError.nodes,
source: originalError.source,
positions: originalError.positions,
path: path == null ? originalError.path : path,
originalError,
extensions: originalError.extensions,
extensions: info
? {
...originalError.extensions,
schemaCoordinates: `${info.parentType.name}.${info.fieldName}`,
}
: originalError.extensions,
});

if (info) {
addSchemaCoordinateToError(error, info);
}

return error;
}
Loading