pear-devs/pear-desktop · error · Error

Failed to get token

Error message

Failed to get token

What it means

'Failed to get token' is thrown by MusixMatch's init() when getToken() returns a falsy value — i.e. the provider could not extract a valid token from MusixMatch's token endpoint. init() first checks a localStorage-cached token (valid for 60 seconds), removes it if expired, then fetches a fresh one. Because the constructor kicks off init, this error typically surfaces on the first query() call via 'Token not initialized' or as an unhandled rejection.

Source

Thrown at src/plugins/synced-lyrics/providers/MusixMatch.ts:271

      token: z.string(),
      expires: z.number(),
    }),
  ]);

  private key = 'ytm:synced-lyrics:mxm:token';
  private async init() {
    const { token, expires } = this.savedTokenSchema.parse(
      JSON.parse(localStorage.getItem(this.key) ?? '{ "token": null }'),
    );
    if (token && expires > Date.now()) {
      this.token = token;
      return;
    }

    localStorage.removeItem(this.key);

    this.token = await this.getToken();
    if (!this.token) throw new Error('Failed to get token');

    localStorage.setItem(
      this.key,
      JSON.stringify({ token: this.token, expires: Date.now() + (60 * 1000) }),
    );
  }

  private tokenSchema = z.object({
    message: z.object({
      body: z
        .object({
          user_token: z.string(),
        })
        .optional(),
    }),
  });
  private async getToken() {
    const endpoint = 'token.get';

View on GitHub (pinned to 1e2aac5706)

Solutions

  1. Verify network connectivity and that the MusixMatch token URL used in getToken() is still reachable.
  2. Log the raw token response in getToken() and compare it against savedTokenSchema; update the parsing if MusixMatch changed the format.
  3. If token fetch is inherently unreliable in your environment, disable the MusixMatch provider and fall back to other lyrics providers.
  4. Catch the init failure and schedule reinit() with backoff instead of letting the rejection propagate.

Example fix

// before
this.token = await this.getToken();
if (!this.token) throw new Error('Failed to get token');

// after
this.token = await this.getToken();
if (!this.token) {
  console.warn('MusixMatch token fetch failed; provider disabled until reinit');
  throw new Error('Failed to get token');
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight reachability before relying on the provider
async function canReachMusixMatch(): Promise<boolean> {
  try {
    await fetch(MUSIXMATCH_TOKEN_URL, { method: 'HEAD' });
    return true;
  } catch {
    return false;
  }
}

Type guard

const isTokenFetchFailure = (e: unknown): boolean =>
  e instanceof Error && e.message === 'Failed to get token';

Try / catch

try {
  await provider.reinit();
} catch (e) {
  if (isTokenFetchFailure(e)) {
    provider.enabled = false; // or skip provider for this session
    return fallbackProvider;
  }
  throw e;
}

Prevention

When it happens

Trigger: First use of the MusixMatch provider, or 60s after the last token was cached, when the network request for the token fails or the returned content doesn't match savedTokenSchema (z.union).

Common situations: No internet/DNS failure, MusixMatch changing the token endpoint or the token page's markup, corporate proxy blocking the domain, or the extracted token value being empty. Also fires whenever reinit() is called after the cached entry was removed.

Related errors


AI-assisted analysis of pear-devs/pear-desktop@1e2aac5706 (2026-08-27). Data as JSON: /api/errors/044cc8297f344f4b. Report an issue: GitHub.