Skip to content

fix(analytics-core): coerce non-string apiKey in remote config localStorage key#1764

Open
Mercy811 wants to merge 1 commit into
mainfrom
claude/fix-remote-config-localstorage-substring
Open

fix(analytics-core): coerce non-string apiKey in remote config localStorage key#1764
Mercy811 wants to merge 1 commit into
mainfrom
claude/fix-remote-config-localstorage-substring

Conversation

@Mercy811
Copy link
Copy Markdown
Contributor

Summary

When a caller passes a non-string apiKey to amplitude.init (undefined, null, a number, etc), RemoteConfigLocalStorage's constructor calls apiKey.substring(0, 10) and crashes with TypeError: e.substring is not a function. Because the constructor runs inside an async _init, the failure surfaces as an unhandled promise rejection in the customer's app, then aborts the rest of SDK init.

Volume: 399 events / 7d on the SDK Diagnostics – Browser SDK dashboard.

Example logs

What the customer sees

  • An uncaught TypeError: e.substring is not a function in their browser console and any RUM/error-monitoring they run
  • amplitude.init(...) rejects; subsequent amplitude.track(...) calls never get a real client and quietly do nothing (or queue forever)

Fix

Coerce non-string apiKey to an empty string when building the localStorage key. The downstream remote-config fetch will still fail (because the apiKey is genuinely invalid), but in a structured way — logged as a 4xx with isLastFetchInvalidApiKey = true — instead of throwing a confusing TypeError that aborts the rest of init.

Test plan

  • pnpm --filter @amplitude/analytics-core test — 797 pass, 100% coverage maintained
  • pnpm --filter @amplitude/analytics-core lint — clean (pre-existing warnings only)
  • Monitor dashboard after release for drop in remote-config-localstorage.ts:9 events

🤖 Generated with Claude Code

…torage key

When customers pass a non-string apiKey to amplitude.init (undefined, null,
a number, etc), the RemoteConfigLocalStorage constructor crashes with
"e.substring is not a function". Surfaces as an unhandled promise rejection
because init is async. ~399 events/7d on the dashboard.

Coerce to empty string when apiKey isn't a string so the key prefix is
stable and init can continue; the downstream fetch will fail on its own
with a clearer signal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff735bca55

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


constructor(apiKey: string, logger: ILogger) {
this.key = `AMP_remote_config_${apiKey.substring(0, 10)}`;
const apiKeyString = typeof apiKey === 'string' ? apiKey : '';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard the later diagnostics storage construction too

For browser init calls that pass a non-string API key such as null or a number, this fallback prevents RemoteConfigLocalStorage from throwing, but the same _init path then constructs DiagnosticsClient, whose DiagnosticsStorage constructor still executes apiKey.substring(0, 10). In IndexedDB-capable browsers, the init promise can therefore still reject with the same TypeError after the remote-config subscription completes, so the customer-visible failure this change targets is not actually fixed for those inputs.

Useful? React with 👍 / 👎.

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.

1 participant