HarperFast / HarperFast/harper
graphqlSchema component load swallows schema errors into a 30s timeout
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
## Problem
Any error thrown while processing a GraphQL schema during component load is swallowed into a generic 30s timeout, with the real reason absent from operator-visible logs. The operator only ever sees:
```
Error: Could not load component 'graphqlSchema' for application '' due to: handleApplication timed out after 30000ms for graphqlSchema on behalf of
```
This affects **every** schema-processing error — an unknown directive, a bad type, a malformed schema, or (the case that surfaced this) a `@table(database:)` using a reserved database name. In each case the component fails correctly (nothing is persisted), but there is a 30s hang and no indication of *why*.
## Root cause
`resources/graphql.ts:64-85` — `handleApplication` returns `once(entryHandler, 'initialLoadComplete')`, which waits **only** on the success event:
```ts
const initialLoadPromise = once(entryHandler, 'initialLoadComplete');
initialLoadPromise.then(() => { initialLoadComplete = true; });
return initialLoadPromise;
```
When the async entry handler rejects (`processGraphQLSchema` → `table()` throws, `resources/graphql.ts:317`), `Scope` logs and re-emits the rejection as a scope `'error'` event (`components/Scope.ts:401-404`) but never reaches `emit('initialLoadComplete')` (`Scope.ts:416`). So the promise `handleApplication` returned never settles, and the `Promise.race` in `components/componentLoader.ts:282` falls through to the 30s watchdog.
The rejecting promise `handleEntry` builds internally (`Scope.ts:411`) already fails correctly — the graphql plugin just re-derives its own success-only listener instead of using it.
## Suggested fix
Propagate the entry-handler rejection so a schema error fails fast with its real message. Either:
- race `'initialLoadComplete'` against the scope `'error'` event in `handleApplication`, or
- return the rejecting promise `handleEntry` already produces (`Scope.ts:411`) rather than a fresh success-only `once(...)`.
Either way the component should fail immediately with the underlying error (e.g. `'super_user' is a reserved name and cannot be used as a database name`) instead of a 30s timeout.
## Test
Integration test: a component whose schema throws during processing (e.g. a reserved-name `@table(database:)`, or a malformed schema) surfaces the underlying error promptly, not a `handleApplication timed out` after 30s.
## Context
Found while runtime-verifying #1913 (reject reserved role-permission names as database names). #1913's reserved-name check on the authoring path is correct and unaffected — it's this loader-level error surfacing that turns the rejection (and every other schema error) into an undiagnosable timeout.
Contributor guide
Assessment
This issue has not been assessed yet.