slymnoyann/hey-1 · critical · Error

Missing tokens in refresh response

Error message

Missing tokens in refresh response

What it means

Thrown when the refresh mutation does return an AuthenticationTokens payload, but either accessToken or refreshToken within it is empty/undefined. The server acknowledged the refresh request and typed the response as tokens, yet the actual credentials are missing, so signIn cannot be performed safely.

Source

Thrown at src/helpers/tokenManager.ts:29

  try {
    for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
      const { data } = await apolloClient.mutate<RefreshMutation>({
        mutation: RefreshDocument,
        variables: { request: { refreshToken } }
      });

      const refreshResult = data?.refresh;

      if (!refreshResult) {
        throw new Error("No response from refresh");
      }

      if (refreshResult.__typename === "AuthenticationTokens") {
        const { accessToken: newAccessToken, refreshToken: newRefreshToken } =
          refreshResult;

        if (!newAccessToken || !newRefreshToken) {
          throw new Error("Missing tokens in refresh response");
        }

        signIn({
          accessToken: newAccessToken,
          refreshToken: newRefreshToken
        });

        return newAccessToken;
      }

      if (refreshResult.__typename === "ForbiddenError") {
        signOut();
        throw new Error("Refresh token is invalid or expired");
      }

      if (attempt < MAX_RETRIES - 1) {
        await new Promise((resolve) =>
          setTimeout(resolve, 2 ** attempt * 1000)

View on GitHub (pinned to 88c8f9d553)

Solutions

  1. Inspect the raw refresh response in the network tab to confirm which token field is missing
  2. Align the client mutation selection set with the current API schema (both accessToken and refreshToken selected)
  3. If the server is yours, fix the resolver to always return both tokens or return an error typename instead
  4. Treat this as a hard auth failure: signOut() before throwing so the user is not stuck in a broken session

Example fix

// before
if (!newAccessToken || !newRefreshToken) {
  throw new Error("Missing tokens in refresh response");
}

// after
if (!newAccessToken || !newRefreshToken) {
  signOut();
  throw new Error("Missing tokens in refresh response");
}
Defensive patterns

Strategy: type-guard

Validate before calling

// nothing caller-side can validate pre-flight beyond having a well-formed refresh token
if (!refreshToken) signOut();

Type guard

const isCompleteTokens = (r: unknown): r is { __typename: "AuthenticationTokens"; accessToken: string; refreshToken: string } =>
  typeof r === "object" && r !== null &&
  r.__typename === "AuthenticationTokens" &&
  typeof (r as { accessToken?: unknown }).accessToken === "string" &&
  typeof (r as { refreshToken?: unknown }).refreshToken === "string";

Try / catch

try {
  await refreshTokens(refreshToken);
} catch (e) {
  if (e instanceof Error && e.message === "Missing tokens in refresh response") {
    signOut();
    window.location.href = "/login";
  }
  throw e;
}

Prevention

When it happens

Trigger: API returns { __typename: 'AuthenticationTokens', accessToken: null } or omits refreshToken; a schema change making one token field nullable; or a partially successful server-side session regeneration where one token fails to mint.

Common situations: Backend version mismatch after a deploy that altered the refresh response shape, server-side JWT signing failure producing null fields, or a mocked/test server returning an incomplete AuthenticationTokens object.

Related errors


AI-assisted analysis of slymnoyann/hey-1@88c8f9d553 (2026-08-28). Data as JSON: /api/errors/54197226afa0c029. Report an issue: GitHub.