decolua/9router · error · Error

Zed credential is missing userId or accessToken

Error message

Zed credential is missing userId or accessToken

What it means

buildZedUserAuthHeader builds the Zed `Authorization: "<userId> <accessToken>"` header from credentials. It reads userId from providerSpecificData.userId or credentials.userId, and the token from credentials.accessToken or credentials.apiKey. If either is absent it throws, since Zed's user-auth endpoints cannot be called without both parts.

Source

Thrown at open-sse/shared/zedAuth.js:164

      return crypto
        .privateDecrypt(
          { key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING },
          encrypted,
        )
        .toString("utf8");
    } catch {
      const message = oaepError instanceof Error ? oaepError.message : String(oaepError);
      throw new Error(`Failed to decrypt Zed access token: ${message}`);
    }
  }
}

export function buildZedUserAuthHeader(credentials) {
  const psd = credentials?.providerSpecificData || {};
  const userId = psd.userId || credentials?.userId;
  const accessToken = credentials?.accessToken || credentials?.apiKey;
  if (!userId || !accessToken) {
    throw new Error("Zed credential is missing userId or accessToken");
  }
  return `${userId} ${accessToken}`;
}

function getSystemId(credentials) {
  return String(
    credentials?.providerSpecificData?.systemId || credentials?.systemId || "",
  );
}

async function fetchJson(url, options, proxyOptions = null) {
  const res = await proxyAwareFetch(url, options, proxyOptions);
  const text = await res.text();
  let data = null;
  if (text) {
    try {
      data = JSON.parse(text);
    } catch {

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Complete the Zed OAuth sign-in so the credential stores userId (in providerSpecificData or top-level) and accessToken.
  2. Check credentials.providerSpecificData.userId and credentials.accessToken directly before calling; fill in whatever is missing.
  3. If you have an apiKey instead of an OAuth token, ensure it is set on credentials.accessToken or credentials.apiKey — the header builder accepts apiKey as the token.
  4. Re-save the credential in the dashboard to make sure partial data wasn't persisted.

Example fix

// before
buildZedUserAuthHeader({ accessToken: "tok" }); // no userId
// after
buildZedUserAuthHeader({ providerSpecificData: { userId: "42" }, accessToken: "tok" });
Defensive patterns

Strategy: validation

Validate before calling

function canBuildZedHeader(cred) {
  const psd = cred?.providerSpecificData || {};
  return Boolean((psd.userId || cred?.userId) && (cred?.accessToken || cred?.apiKey));
}
// guard: if (!canBuildZedHeader(credentials)) prompt OAuth sign-in before any Zed API call;

Type guard

const hasZedCredential = (c) => Boolean(c && (c.providerSpecificData?.userId || c.userId) && (c.accessToken || c.apiKey));

Try / catch

let authHeader;
try {
  authHeader = buildZedUserAuthHeader(credentials);
} catch (e) {
  // credential incomplete — kick off Zed OAuth sign-in flow to fill userId/accessToken
}

Prevention

When it happens

Trigger: Calling fetchZedAuthenticatedUser/fetchZedLlmToken with a credentials object that lacks userId or accessToken/apiKey — e.g. an API-key-only credential that never went through the Zed OAuth callback, or a partially-saved credential row.

Common situations: User added a Zed provider with only an API key (no OAuth sign-in); credential sync/import dropped providerSpecificData.userId; the decrypt step failed earlier so accessToken was never persisted.

Related errors


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