decolua/9router · error

Vertex: failed to mint access token from Service Account JSO

Error message

Vertex: failed to mint access token from Service Account JSON

What it means

Thrown in VertexExecutor.execute (open-sse/executors/vertex.js:138) when refreshVertexToken fails to mint a Bearer access token from the stored Service Account JSON. Token minting (JWT assertion signed with the SA private key, exchanged at Google's OAuth2 token endpoint) is delegated to tokenRefresh.js; a null/empty accessToken result means the exchange did not produce a usable token.

Source

Thrown at open-sse/executors/vertex.js:138

  async refreshCredentials(credentials, log) {
    const saJson = parseVertexSaJson(credentials?.apiKey);
    if (!saJson) return null;

    const result = await refreshVertexToken(saJson, log);
    if (!result) return null;

    return { accessToken: result.accessToken, expiresAt: result.expiresAt };
  }

  async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {
    const saJson = parseVertexSaJson(credentials?.apiKey);
    const adcJson = parseVertexAdcJson(credentials?.apiKey);

    // SA JSON flow: mint Bearer token via JWT assertion (cached)
    if (saJson) {
      const result = await refreshVertexToken(saJson, log);
      if (!result?.accessToken) throw new Error("Vertex: failed to mint access token from Service Account JSON");
      credentials.accessToken = result.accessToken;
    }

    // ADC user credential flow: refresh Bearer token via Google OAuth2 token endpoint
    if (adcJson) {
      const result = await refreshGoogleToken(
        adcJson.refresh_token,
        adcJson.client_id,
        adcJson.client_secret,
        log
      );
      if (!result?.accessToken) throw new Error("Vertex: failed to refresh access token from ADC JSON (authorized_user)");
      credentials.accessToken = result.accessToken;
    }

    // vertex-partner with raw key: auto-resolve project_id if not provided
    if (this.provider === "vertex-partner" && !saJson && !adcJson && !credentials?.providerSpecificData?.projectId) {
      const projectId = await resolveProjectId(credentials.apiKey);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Re-download a fresh Service Account JSON key from GCP IAM (Keys → Add key → JSON) and re-save it as the credential's apiKey, keeping the private_key newlines intact.
  2. Check connectivity/proxy to https://oauth2.googleapis.com/token — the mint request must reach Google.
  3. Verify the service account is enabled and the key is not expired/revoked in the GCP console.
  4. Check logs from refreshVertexToken (the log object passed in) for the underlying Google error (e.g. invalid_grant, invalid_scope) and fix accordingly.
Defensive patterns

Strategy: try-catch

Validate before calling

const sa = parseVertexSaJson(creds?.apiKey);
if (sa) {
  const required = ["client_email", "private_key", "project_id"];
  const missing = required.filter(k => !sa[k]);
  if (missing.length) throw new Error(`SA JSON missing fields: ${missing.join(", ")}`);
}

Type guard

function isValidSaJson(v) {
  if (typeof v !== "string") return false;
  try {
    const j = JSON.parse(v);
    return j.type === "service_account" && typeof j.private_key === "string" && j.private_key.includes("PRIVATE KEY") && !!j.client_email;
  } catch { return false; }
}

Try / catch

try {
  await vertexChat(model, body);
} catch (e) {
  if (/failed to mint access token from Service Account JSON/.test(e.message)) {
    log.error("SA token mint failed — check key validity, clock skew, and oauth2.googleapis.com reachability");
    await reauthFlow();
  } else throw e;
}

Prevention

When it happens

Trigger: execute() is called, credentials.apiKey parses as a Service Account JSON (parseVertexSaJson), refreshVertexToken(saJson, log) resolves to null or an object without accessToken — e.g. JWT signing failed (bad private_key), Google's token endpoint rejected the assertion (invalid_grant), the network call failed, or the SA key was revoked/disabled.

Common situations: Service account key was deleted or disabled in GCP IAM; the SA JSON was copied incompletely (truncated private_key, missing client_email); system clock skew invalidates the JWT iat/exp; outbound access to oauth2.googleapis.com is blocked by firewall/proxy; key file was re-encoded and newlines in private_key were mangled.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/6980021108531b1c. Report an issue: GitHub.