aws / aws/graph-explorer

Consolidate domain error types behind a shared base class

Open
#2,163 0 comments 0 reactions 0 assignees View on GitHub
internal tech debt
Dominant language
TypeScript
Stars
481
Forks
108
Avg merge
6d 8h
Merged PRs (30d)
5

Description

## Goal

Domain errors each define their own shape and each need a hand-written branch in the display paths. `QueryValueError` (`connector/queryValueError.ts`) is the only one enforcing a contract: an abstract `details: object`, plus a name passed as a string literal because the production build minifies class names. `StylingParseError`, `FileEnvelopeError`, `NetworkError`, `ServerConnectionError`, and `ConnectionLinkError` each roll their own.

The cost is concentrated in `utils/createDisplayError.ts`: a ~190-line `if`-chain that must be edited whenever a new error type appears, and which silently degrades to "Something went wrong" when someone forgets. `utils/createErrorDetails.ts` has a smaller version of the same problem. The number of domain error types is expected to keep growing, so the per-type branch is a recurring tax rather than a one-off.

Decide how to consolidate, and how far.

## Directions to explore

Not an exhaustive list — the point of the spike is to find the best mechanism, and type-level approaches (generics, discriminated unions, mapped or template-literal types, exhaustiveness checking) may beat a class hierarchy outright.

- **A shared base class.** Extract a base from `QueryValueError` (name literal + abstract `details`) and have the domain errors extend it, so a single branch in `createErrorDetails` serves every error and each new type gets the details dialog for free.
- **Errors owning their display text.** Put the user-facing title and message on the error itself, inverting `createDisplayError` from a growing `if`-chain into "ask the error, otherwise map platform and third-party errors". Turns a missing case into a compile error instead of a generic message.
- **Type-level enforcement without inheritance.** A registry, a discriminated union over a `kind` field, or a generic wrapper could enforce the details shape and exhaustive handling without asking every error to extend a common ancestor — worth comparing on ergonomics and on how well it survives minification.

Whatever the mechanism, it has to keep enforcing the name-as-literal rule. Minified class names would otherwise surface as `Error` in the details dialog.

## Constraint: import cycles

This shapes where any shared type can live. The display helpers are in `utils/` and the error types in `core/`, and `core` modules commonly import the `@/utils` barrel. Defining `ConnectionLinkError` in `core/urlConnectionParams.ts` created `utils/createDisplayError` → `core/urlConnectionParams` → `@/utils`, which broke 10 unrelated test files with `Cannot access '__vite_ssr_import_2__' before initialization`. It was fixed by moving the class to `core/connectionLinkError.ts`, a leaf module importing nothing from the app.

So: every error type the display paths recognize must be reachable without pulling in app modules, and the same goes for a shared base. Deciding where that base belongs — `utils/`, `core/`, or `@shared` — is part of this spike, not an afterthought.

## Expected Outcome

A recommended mechanism with a rough size estimate, a decision on where the shared type lives, and a tasked-out plan. An ADR if the outcome changes how errors are modelled.

## Related Issues

- Originated from #1828

> [!IMPORTANT]
> Internal only — this issue is maintained by the core team and is not accepting external contributions.

Contributor guide

Open the contributing guide

Research direction

Start by reading utils/createDisplayError.ts and utils/createErrorDetails.ts, then inspect the listed error classes, especially connector/queryValueError.ts and core/connectionLinkError.ts. Compare the proposed inheritance, error-owned display text, and type-level approaches while tracing the import-cycle constraint. Done means a recommended mechanism, shared-type location, rough size estimate, tasked-out plan, and an ADR if error modelling changes.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.