HarperFast / HarperFast/harper

graphqlSchema component load swallows schema errors into a 30s timeout

Open
#1,917 1 comment 0 reactions 1 assignee Claimed by @kylebernhardy View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.