openclaw/openclaw · error · Error

Chutes OAuth credential is missing refresh token

Error message

Chutes OAuth credential is missing refresh token

What it means

Thrown by refreshChutesOAuthCredential when the stored OAuthCredential's refresh field is empty or absent after normalization. This function is called to renew an expired access token; without a refresh_token it cannot perform the grant. This indicates the stored credential is incomplete or corrupted — either it was never granted a refresh token, or it was lost during storage/migration.

Source

Thrown at extensions/chutes/oauth.ts:240

  }
  return {
    access: token.access,
    refresh: token.refresh,
    expires: token.expires,
    email: info?.username,
    accountId: info?.sub,
    clientId: params.app.clientId,
  } as ChutesStoredOAuth;
}

/** Refreshes a stored Chutes OAuth credential through the provider token endpoint. */
export async function refreshChutesOAuthCredential(
  credential: OAuthCredential,
  options: { fetchFn?: typeof fetch; now?: number } = {},
): Promise<OAuthCredential> {
  const refreshToken = normalizeOptionalString(credential.refresh);
  if (!refreshToken) {
    throw new Error("Chutes OAuth credential is missing refresh token");
  }

  const clientId = normalizeOptionalString(credential.clientId ?? process.env.CHUTES_CLIENT_ID);
  if (!clientId) {
    throw new Error("Missing CHUTES_CLIENT_ID for Chutes OAuth refresh (set env var or re-auth).");
  }
  const clientSecret = normalizeOptionalString(process.env.CHUTES_CLIENT_SECRET);
  const body = new URLSearchParams({
    grant_type: "refresh_token",
    client_id: clientId,
    refresh_token: refreshToken,
  });
  if (clientSecret) {
    body.set("client_secret", clientSecret);
  }

  const token = await requestChutesTokenGrant({
    body,

View on GitHub (pinned to 01804a7531)

Solutions

  1. Re-run the Chutes OAuth onboarding flow to obtain a fresh credential with a refresh token
  2. Inspect the stored credential (auth-profiles.json for the agent) to confirm the refresh field is missing
  3. If this affects many agents, check whether a migration was supposed to handle old credentials
Defensive patterns

Strategy: try-catch

Validate before calling

function hasValidRefreshCredential(cred: OAuthCredential): boolean {
  return typeof cred.refresh === "string" && cred.refresh.trim().length > 0;
}
// Before calling refreshChutesOAuthCredential
if (!hasValidRefreshCredential(storedCred)) {
  await reauthenticateChutes(); // trigger fresh onboarding
  return;
}

Type guard

function hasValidRefreshCredential(cred: OAuthCredential): cred is OAuthCredential & { refresh: string } {
  return typeof cred.refresh === "string" && cred.refresh.trim().length > 0;
}

Try / catch

try {
  const refreshed = await refreshChutesOAuthCredential(credential);
} catch (e) {
  if (e instanceof Error && e.message.includes("missing refresh token")) {
    // Credential is incomplete; prompt re-authentication
    await triggerChutesReauth(agentId);
  } else { throw e; }
}

Prevention

When it happens

Trigger: A stored Chutes credential that was created by an older flow that did not persist the refresh token. Credential corruption in the auth-profiles store. Manual credential import that omitted the refresh field. A prior token exchange that did not return a refresh token but was stored anyway (older plugin version before the check at line 645 was added).

Common situations: Upgrading from an older OpenClaw version that stored Chutes credentials differently. Importing credentials from another tool. Storage migration that dropped fields. Corrupt auth-profiles.json after a crash.

Related errors


AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12). Data as JSON: /api/errors/66b5789bb79e942e. Report an issue: GitHub.