different-ai/openwork · error · Error

Missing Google OAuth configuration: ${missing.join(", ")}

Error message

Missing Google OAuth configuration: ${missing.join(", ")}

What it means

The Google Workspace extension throws this when resolving OAuth credentials via googleWorkspaceCredentials() before exchanging an authorization code for tokens. The helper reads GOOGLE_WORKSPACE_CLIENT_ID / GOOGLE_WORKSPACE_CLIENT_SECRET (with legacy fallbacks) from the environment; if the client secret is absent and no token broker URL is configured, the missing list is non-empty and the exchange is refused. The bundled desktop client ID is used by default, so this is almost always a missing/empty secret or a secret env var typo when running a custom client.

Source

Thrown at apps/server/src/extensions/google-workspace.ts:569

            ? payload.error
            : response.statusText
      : response.statusText;
    throw new Error(`Google request failed (${response.status}): ${details}`);
  }
  return payload;
}

async function fetchGoogleUserInfo(accessToken: string) {
  return fetchGoogleJson("https://www.googleapis.com/oauth2/v3/userinfo", { headers: { Authorization: `Bearer ${accessToken}` } });
}

async function fetchGoogleWorkspaceTokenBrokerJson(tokenBrokerUrl: string, body: Record<string, unknown>) {
  return fetchGoogleJson(tokenBrokerUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
}

async function exchangeGoogleWorkspaceCode(input: { code: string; redirectUri: string; verifier: string }) {
  const { clientId, clientSecret, tokenBrokerUrl, missing } = googleWorkspaceCredentials();
  if (missing.length > 0) throw new Error(`Missing Google OAuth configuration: ${missing.join(", ")}`);
  if (tokenBrokerUrl) {
    return fetchGoogleWorkspaceTokenBrokerJson(tokenBrokerUrl, {
      grantType: "authorization_code",
      provider: GOOGLE_WORKSPACE_EXTENSION_ID,
      clientId,
      code: input.code,
      codeVerifier: input.verifier,
      redirectUri: input.redirectUri,
    });
  }
  return fetchGoogleJson("https://oauth2.googleapis.com/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      client_id: clientId,
      client_secret: clientSecret,
      code: input.code,
      code_verifier: input.verifier,

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Set the GOOGLE_WORKSPACE_CLIENT_SECRET environment variable (or the legacy variant it falls back to) to your OAuth client secret and restart the server
  2. Alternatively set GOOGLE_WORKSPACE_TOKEN_BROKER_URL so tokens are exchanged via the token broker and no secret is needed locally
  3. Check the exact env var names against googleWorkspaceCredentials() in apps/server/src/extensions/google-workspace.ts:321 — whitespace-only values are treated as missing
  4. Call the connect status endpoint (googleWorkspaceStatusPayload reports `configured`/`missing`) before starting a connect flow

Example fix

// before
GOOGLE_WORKSPACE_CLIENT_ID=my-client.apps.googleusercontent.com
google oauth connect ...
// Error: Missing Google OAuth configuration: GOOGLE_WORKSPACE_CLIENT_SECRET

// after
GOOGLE_WORKSPACE_CLIENT_ID=my-client.apps.googleusercontent.com
GOOGLE_WORKSPACE_CLIENT_SECRET=GOCSPX-xxxx
google oauth connect ... // succeeds
Defensive patterns

Strategy: validation

Validate before calling

const credentials = googleWorkspaceCredentials();
if (credentials.missing.length > 0) {
  throw new Error(`Configure before connecting Google Workspace: missing ${credentials.missing.join(", ")}`);
}
// or without internals:
if (!process.env.GOOGLE_WORKSPACE_CLIENT_SECRET && !process.env.GOOGLE_WORKSPACE_TOKEN_BROKER_URL) {
  throw new Error("Set GOOGLE_WORKSPACE_CLIENT_SECRET or GOOGLE_WORKSPACE_TOKEN_BROKER_URL first");
}

Type guard

function isGoogleOauthConfigured(): boolean {
  return Boolean(
    process.env.GOOGLE_WORKSPACE_CLIENT_SECRET?.trim() ||
    process.env.GOOGLE_WORKSPACE_TOKEN_BROKER_URL?.trim(),
  );
}

Try / catch

try {
  await startGoogleWorkspaceConnect();
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Missing Google OAuth configuration")) {
    showSetupGuide(err.message.split(": ")[1]?.split(", ") ?? []);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the OAuth connect flow (exchangeGoogleWorkspaceCode with a valid code/redirectUri/verifier) when googleWorkspaceCredentials().missing is non-empty — i.e. neither GOOGLE_WORKSPACE_CLIENT_SECRET nor its legacy variant is set AND GOOGLE_WORKSPACE_TOKEN_BROKER_URL is unset.

Common situations: Self-hosting the OpenWork server and forgetting to export the client secret env var; typos or empty-string env values (whitespace is trimmed, so ' ' counts as missing); migrating deployments where the legacy secret env var name was renamed; pointing the client at a custom OAuth client ID without providing its secret.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/93bdd952804e4c9c. Report an issue: GitHub.