getBuiltMesh() caches rejected promise permanently, causing all subsequent requests to fail with "Schemas couldn't be generated successfully"
### Issue workflow progress
<!-- PLEASE DO NOT REMOVE THIS SECTION -->
_Progress of the issue based on the
[Contributor Workflow](https://github.com/the-guild-org/Stack/blob/master/CONTRIBUTING.md#a-typical-contributor-workflow)_
- [ ] 1. The issue provides a reproduction available on
[Github](https://github.com/ardatan/graphql-mesh/tree/master/examples/hello-world),
[Stackblitz](https://stackblitz.com/github/ardatan/graphql-mesh/tree/master/examples/hello-world)
or
[CodeSandbox](https://codesandbox.io/s/github/ardatan/graphql-mesh/tree/master/examples/hello-world)
> Make sure to fork this template and run `yarn generate` in the terminal.
>
> Please make sure Mesh package versions under `package.json` matches yours.
- [ ] 2. A failing test has been provided
- [ ] 3. A local solution has been provided
- [ ] 4. A pull request is pending review
---
**Describe the bug**
When `getMesh()` fails during initialization (e.g. a transient network error fetching a remote schema), the rejected promise is stored in `meshInstance$` and never cleared. Every subsequent request to that process returns the cached rejection, the service never recovers without a full restart.
With fork > 1, this creates intermittent 500s: workers that happened to initialize successfully continue serving requests, while workers whose first initialization failed are permanently broken. From the outside this looks like a random 50% (or 25%, 75%, etc.) failure rate that "fixes itself" on reload.
**To Reproduce** Steps to reproduce the behavior:
1. Configure a GraphQL Mesh service with fork: 4 and a remote source handler (e.g. fetching SDL from GraphQL Hive CDN)
2. Simulate a transient failure on startup for one or more workers (network blip, upstream timeout, etc.)
3. Observe that affected workers return 500 indefinitely with:
```bash
Error: Schemas couldn't be generated successfully. Check for the logs by running Mesh with DEBUG=1 environmental variable to get more verbose output.
at getMesh (file:///app/node_modules/@graphql-mesh/runtime/esm/get-mesh.js:118:15)
```
**Expected behavior**
If getMesh() rejects, meshInstance$ should be reset to undefined so the next incoming request triggers a fresh initialization attempt.
**Root cause**
The auto-generated `getBuiltMesh()` in `.mesh/index.ts` assigns the initialization promise to the module-level `meshInstance$` but has no `.catch()` on that assignment:
```typescript
let meshInstance$: Promise<MeshInstance> | undefined;
export function getBuiltMesh(): Promise<MeshInstance> {
if (meshInstance$ == null) {
// ...
meshInstance$ = getMeshOptions()
.then(meshOptions => getMesh(meshOptions))
.then(mesh => {
const id = mesh.pubsub.subscribe('destroy', () => {
meshInstance$ = undefined; // only resets on graceful destroy
mesh.pubsub.unsubscribe(id);
});
return mesh;
});
// ← no .catch() — rejected promise is cached here permanently
}
return meshInstance$; // ← returns the rejected promise on every subsequent call
}
```
When the promise rejects, meshInstance$ is set to the rejected promise. On the next request, `meshInstance$ == null` is false (a rejected Promise is not null), so the guard never triggers a retry. The rejected promise is returned forever.
**Environment:**
- @graphql-mesh/cli: 0.100.26
- @graphql-mesh/runtime: (via @graphql-mesh/cli)
- @graphql-mesh/graphql: 0.97.5
- Node.js, fork: 4
- Remote source: GraphQL Hive CDN SDL
**Additional context**
<!-- Add any other context about the problem here. -->
关闭于 2026-03-18 1 条评论