Skip to content

Network errors are silently swallowed, error states offer no recovery path #201

@simihartstein

Description

@simihartstein

Summary

Components and hooks across @itwin/imodel-browser-react and @itwin/manage-versions-react have two distinct classes of error-handling problems, depending on whether the failing operation is a primary data load or a secondary background action.

Specific Problems

Primary data fetches — error state surfaces in UI, but recovery is impossible

Location Behavior
useIModelData On fetch failure, sets DataStatus.FetchFailed and calls console.error(e)
useITwinData Same
ManageVersions getVersions Sets RequestStatus.Failed (silently — error object is discarded in the .catch)
ManageVersions getChangesets Same

These do surface in the UI (an error string is shown), but:

  • The user has no "Try again" button. The grid is stuck until the consumer unmounts and remounts the component.
  • No onError callback is exposed, so the consumer cannot observe what went wrong or log it with context.
  • ManageVersions discards the error object entirely in the .catch(() => setVersionStatus(RequestStatus.Failed)) pattern, losing the stack trace.

Secondary/background operations — truly silently swallowed

Location Behavior
useITwinFavorites — add, remove, and initial fetch Caught, console.error(error) called, nothing else. No user feedback.
IModelGrid.tsx — add to recents Caught, console.error("Failed to add iModel to recents", e) called.
iModelApi.tsremoveIModelFromRecents Same.
ManageVersions.tsx — user data fetch .catch(() => { console.error("Unable to fetch users data"); }) — error object discarded.
VersionsTab.tsx — expand row (fetch changesets) .catch(() => { console.error("Failed to get Changesets"); }) — error object discarded, sub-rows silently stay empty.

These are fire-and-forget or best-effort operations where swallowing is sometimes intentional, but there is zero user feedback even for operations the user explicitly triggered (e.g., adding/removing a favorite).

Recommended Fix

1. Add "Try again" / retry actions to primary error states.

NoResults (imodel-browser) and the empty-table overlay in manage-versions should accept an optional onRetry callback. When the status is an error state, render a "Try again" button alongside the error message. The refetchITwins / refetchIModels functions already exist — they just need to be wired into the error UI.

2. Expose an onError callback on data hooks.

useIModelData, useITwinData, and the favorites hooks should accept an onError?: (error: unknown) => void option so consumers can observe failures and log them with appropriate context.

3. Preserve the error object in .catch() handlers.

Several catch handlers receive no argument at all, losing the stack trace and error message entirely. At minimum, pass the error to the log call: .catch((e) => setVersionStatus(RequestStatus.Failed)).catch((e) => { setVersionStatus(RequestStatus.Failed); log?.(e); }).

4. Switch non-critical background operations to console.warn.

Network failures on best-effort calls (add/remove recents, user display-name enrichment) are expected, transient events. console.error implies a programming mistake and triggers error monitoring tools. These should use console.warn.

5. Surface user-triggered secondary failures.

When a user explicitly triggers an action (add/remove favorite), a silent console.error is not acceptable. These should at minimum call an onError callback or show a toast.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions