Skip to content

YPE-1565: Show error when YouVersionProvider has no appKey#264

Open
cameronapak wants to merge 6 commits into
mainfrom
YPE-1565-missing-app-key-error
Open

YPE-1565: Show error when YouVersionProvider has no appKey#264
cameronapak wants to merge 6 commits into
mainfrom
YPE-1565-missing-app-key-error

Conversation

@cameronapak

@cameronapak cameronapak commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator
image

Empty/missing appKey rendered a blank page. Now: UI provider shows a styled "Missing app key" message; hooks provider throws.

  • Styled MissingAppKey panel + Storybook story
  • i18n (en/fr/es), example cleanup, unit + integration tests, changeset

YPE-1565

🤖 Generated with Claude Code

Greptile Summary

This PR surfaces a clear developer error when YouVersionProvider receives a missing or empty appKey, replacing the previous silent blank-page failure. The UI package now renders a styled MissingAppKey panel (with i18n support in en/fr/es), while the hooks-only package throws a descriptive Error.

  • UI provider (packages/ui): Guards on !appKey?.trim() before delegating to the base provider, renders MissingAppKey with correct theme resolution and logs to console.error for developer guidance; includes unit tests, Storybook stories, and the example app updated to remove the empty-string default.
  • Hooks provider (packages/hooks): Throws a descriptive error before executing any context setup, with parametrized unit tests covering undefined, empty string, and whitespace inputs.
  • Changeset: Both packages bumped as minor, correctly reflecting the new user-visible behaviour.

Confidence Score: 4/5

Safe to merge after the open review thread on the hooks provider is resolved; the UI provider path is correct and well-tested.

The hooks provider places its guard throw before the first hook call (useResolvedTheme). If a component ever mounts with a valid key (hooks run) and is then re-rendered with an empty key (throw fires before hooks), React will detect the inconsistent hook count. This was flagged in the previous review thread and remains unaddressed in the current diff, making the hooks provider path unreliable under dynamic appKey changes.

packages/hooks/src/context/YouVersionProvider.tsx — the ordering of the early-exit guard relative to hook invocations needs to be resolved before merging.

Important Files Changed

Filename Overview
packages/hooks/src/context/YouVersionProvider.tsx Adds a throw guard before all hook calls; the throw-before-hooks ordering was flagged in the previous review thread and remains unresolved in this diff.
packages/ui/src/components/YouVersionProvider.tsx Adds a guard that renders MissingAppKey when appKey is empty; useEffect is correctly placed before the conditional return, but console.error is called during render (a side effect).
packages/ui/src/components/missing-app-key.tsx New styled error panel using existing i18n keys; aria-live conflict on role="alert" flagged in previous thread and unresolved.
packages/ui/src/components/YouVersionProvider.test.tsx Comprehensive parametrized tests for undefined, empty string, and whitespace appKey values; correctly asserts on alert role, error text, console.error call, and that children are not rendered.
packages/hooks/src/context/YouVersionProvider.test.tsx Adds three parametrized throw-path tests covering undefined, empty string, and whitespace appKey with ts-expect-error suppression for intentional runtime testing.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[YouVersionProvider rendered] --> B{appKey empty or missing?}
    B -- Yes, UI package --> C[console.error to developer]
    C --> D[Render YvStyles + MissingAppKey panel]
    B -- Yes, hooks-only package --> E[throw Error]
    E --> F[Nearest React Error Boundary]
    B -- No --> G[Render BaseYouVersionProvider]
    G --> H[Children rendered normally]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[YouVersionProvider rendered] --> B{appKey empty or missing?}
    B -- Yes, UI package --> C[console.error to developer]
    C --> D[Render YvStyles + MissingAppKey panel]
    B -- Yes, hooks-only package --> E[throw Error]
    E --> F[Nearest React Error Boundary]
    B -- No --> G[Render BaseYouVersionProvider]
    G --> H[Children rendered normally]
Loading

Reviews (5): Last reviewed commit: "Merge branch 'main' into YPE-1565-missin..." | Re-trigger Greptile

Render a styled "Missing app key" message instead of a blank page; hooks
provider throws for hooks-only consumers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 41e3473

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@youversion/platform-react-hooks Minor
@youversion/platform-react-ui Minor
vite-react Patch
@youversion/platform-core Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/ui/src/components/missing-app-key.tsx Outdated
role="alert" already implies aria-live="assertive"; the explicit
polite value downgraded announcement urgency for a config error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @camrun91, what do we do about locales that we add that aren't yet Crowdin translated? Do we put in potential strings or leave those in English in these locale files?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean new locals? If you are talking adding new strings the process will be to add them to the file in the localization repo. This is a good test to find out the best way to do this as the english source files shouldn't need to be translated and we need a process to make this quicker

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so does the localization repo need a PR and that PR merged in before I can work on this? @camrun91

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this got handled correct?

cameronapak and others added 2 commits June 24, 2026 12:46
- Remove redundant `missingAppKey*` translation keys
- Reuse generic `errorHeading` and `invalidAppKeyError` strings
- Add a `console.error` in `YouVersionProvider` for developers
The previous commit reused errorHeading/invalidAppKeyError and moved the
actionable developer guidance to console.error. Update the assertions to
match: expect "Error" instead of "Missing app key", and verify the
console.error guidance fires. Also refresh the guard comment to reflect
that the panel is intentionally generic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@Dustin-Kelley can you review this? I'm no longer adding new strings to this and re-using old ones

P.S. I'll learn the new strings process and share that with you soon

Comment on lines +87 to 94
if (!appKey?.trim()) {
throw new Error(
'YouVersionProvider: a non-empty "appKey" is required. If you load it from an ' +
'environment variable, make sure it is set and restart your dev server.',
);
}

const resolvedTheme = useResolvedTheme(theme);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Conditional throw placed before hook calls violates Rules of Hooks

The throw guard on line 87 exits the function before useResolvedTheme, useMemo, and useEffect are called (lines 94–116). React requires hooks to be called in the same order on every render. If a component ever receives a valid appKey (hooks run), then is re-rendered with an empty/undefined one (hooks are skipped because the throw fires first), React will detect an inconsistent hook count and either log a rules-of-hooks error or behave unpredictably. The react-hooks/rules-of-hooks ESLint rule flags exactly this pattern.

The guard should be moved to run after all hook invocations, or the component should be split so the hook-bearing path is never entered when appKey is invalid.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/hooks/src/context/YouVersionProvider.tsx
Line: 87-94

Comment:
**Conditional throw placed before hook calls violates Rules of Hooks**

The `throw` guard on line 87 exits the function before `useResolvedTheme`, `useMemo`, and `useEffect` are called (lines 94–116). React requires hooks to be called in the same order on every render. If a component ever receives a valid `appKey` (hooks run), then is re-rendered with an empty/undefined one (hooks are skipped because the throw fires first), React will detect an inconsistent hook count and either log a rules-of-hooks error or behave unpredictably. The `react-hooks/rules-of-hooks` ESLint rule flags exactly this pattern.

The guard should be moved to run after all hook invocations, or the component should be split so the hook-bearing path is never entered when `appKey` is invalid.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants