Skip to main content

Troubleshooting

The iframe doesn't render

Most often:

  • TaxKitProvider isn't actually mounted. The iframe only mounts when the provider is in the React tree. Check that your component hierarchy includes <TaxKitProvider> above wherever you call useTaxKit().

  • Two providers mounted. Each <TaxKitProvider> renders its own iframe — mounting two on the same page produces two iframes, and useTaxKit() consumers only see state from whichever provider is closest in their React tree. Search your tree for accidental nesting.

  • CSP is blocking the frame. Your parent app's Content-Security-Policy frame-src must allow the CoinTracker embedded origin. For production:

    frame-src 'self' https://embedded.cointracker.com;

    Substitute your custom options.apiBaseUrl if you've overridden it.

  • The iframe URL is malformed. Open devtools, find the iframe element, and inspect its src. It should look like https://embedded.cointracker.com/sdk/?config=…. If the config= payload is empty or doesn't decode, your theme / options props are mis-shaped.

  • Browser too old. The kit requires evergreen Chromium, Firefox, Safari (iOS 15+), or Android Chrome 100+. Older browsers will fail at the post-robot bridge layer.

Copy-to-clipboard or subscription checkout silently fails inside the iframe

The iframe requests clipboard-write and payment via its own allow attribute (set by the SDK). If your parent page sets a strict Permissions-Policy header that doesn't delegate those features to iframes, they'll silently fail inside the kit.

Check your response headers — if you have a Permissions-Policy header, make sure it allows delegation. For example:

Permissions-Policy: clipboard-write=(self "https://embedded.cointracker.com"), payment=(self "https://embedded.cointracker.com")

If you don't set a Permissions-Policy header at all, you're fine.

fetchAccessToken is never called

The iframe only requests a token when it's about to call CoinTracker's API. If you're in options.mode: 'mock', no real API calls happen — so fetchAccessToken may never fire. Switch to 'production' or 'alpha' to exercise the real auth path.

If you're in production mode and still don't see fetchAccessToken fire, the iframe probably hasn't completed its handshake yet. Check the post-robot logs in the console for a READY message from the iframe.

TaxKitError.AuthenticationError appears immediately

Either:

  • fetchAccessToken returned null / undefined — your backend handler couldn't mint a token. Check your server logs.
  • fetchAccessToken returned a malformed or expired token. Decode the JWT (e.g. at jwt.io — server-side only, don't paste production tokens into web tools) and verify the exp claim is in the future and the signature is valid against the key CoinTracker provided.
  • The token was minted for the wrong environment. Tokens minted for the alpha environment won't work against production, and vice versa.

Deprecation warnings in the console

Each pre-2.2.0 prop logs:

[TaxKit] `apiBaseUrl` is deprecated and will be removed in a future major version. Use `options.apiBaseUrl` instead.

One warning per prop, deduped per browser session. Migrate to the grouped shape (see Migration) when convenient — they'll be removed in v3.x.

Theme changes don't apply

  • Confirm you're passing theme (the new shape), not themeContract (deprecated). Both work, but if you pass both, the new shape wins — so a half-migrated config can produce surprising results.
  • Confirm your token values are valid CSS color strings (rgb(), hsl(), hex, or named). The iframe doesn't validate — invalid strings just render as the browser default.
  • If you're memoizing theme, confirm the memo actually invalidates when your token values change. Stale useMemo deps will lock the kit to the first theme it saw.

useTaxKit() throws "must be used inside TaxKitProvider"

The hook is reading from a React context that only exists below <TaxKitProvider>. Move your component (or the provider) so the provider wraps the consumer.

If you need to read kit state outside of React render (e.g. from an async function), import the underlying Zustand store:

import { useTaxKitStore } from '@cointracker/tax-kit';

async function someAsyncHandler() {
const { taxKitStatus } = useTaxKitStore.getState();
// …
}

.getState() works outside the React render cycle; the hook does not.

Password manager "Save password?" prompts fire spuriously

This is browser heuristics reacting to the click-then-form-appeared pattern. Set options.defaultOpen: true on dedicated tax pages so the kit renders open on first paint — that breaks the heuristic.

The iframe is stuck on a loading state

Check useTaxKit().errors for an active error:

  • EmbeddedHealthCheckError — the iframe couldn't reach CoinTracker on startup. Check status.cointracker.io.
  • AuthenticationError — see above.
  • GenericError — open a support ticket with reproduction steps.

If errors is empty but the kit still hasn't loaded after 30 seconds, the iframe's GraphQL queries are probably hanging. Open devtools → Network and look for in-flight requests to embedded.cointracker.com.

Still stuck

Reach out to your CoinTracker integration contact with:

  • The partner identifier on your JWT ('coinbase', 'kraken', etc.).
  • The options.mode you're using.
  • A reduced reproduction (the smallest snippet of your integration that triggers the issue).
  • Screenshots of the console errors and the Network tab if relevant.