can1357/oh-my-pi · error · AIError.OAuthError
Z.ai key provisioning failed: no organization/project on acc
Error message
Z.ai key provisioning failed: no organization/project on account
What it means
mintZaiApiKey lists the account's organizations and picks the default (or first) org and project. If no organization or project yields a non-empty organizationId/projectId, key provisioning cannot proceed and OAuthError is thrown. The account simply has no provisioned org/project hierarchy to attach an API key to.
Source
Thrown at packages/ai/src/registry/oauth/zai.ts:174
* `getCustomerInfo` → find/create the OMP-named key → obtain its secret →
* return `${apiKey}.${secretKey}` (the 49-char durable key).
*/
async function mintZaiApiKey(oauthAccessToken: string, fetchImpl: FetchImpl): Promise<string> {
const bizToken = await businessLogin(oauthAccessToken, fetchImpl);
const auth = { Authorization: `Bearer ${bizToken}` };
const customer = unwrapEnvelope(
await getJson(`${BIZ_BASE}/api/biz/customer/getCustomerInfo`, auth, fetchImpl),
"customer lookup",
) as { organizations?: ZaiOrganization[] } | undefined;
const orgs = Array.isArray(customer?.organizations) ? customer.organizations : [];
const org = orgs.find(o => o?.isDefault) ?? orgs[0];
const projects = Array.isArray(org?.projects) ? org.projects : [];
const project = projects.find(p => p?.isDefault) ?? projects[0];
const organizationId = trimmedString(org?.organizationId);
const projectId = trimmedString(project?.projectId);
if (!organizationId || !projectId) {
throw new AIError.OAuthError("Z.ai key provisioning failed: no organization/project on account", {
kind: "token-exchange",
provider: "zai",
});
}
const keysUrl = `${BIZ_BASE}/api/biz/v1/organization/${organizationId}/projects/${projectId}/api_keys`;
const existing = asKeyArray(unwrapEnvelope(await getJson(keysUrl, auth, fetchImpl), "api key list")).find(
key => key.name === KEY_NAME,
);
const keyRecord =
existing ??
(unwrapEnvelope(await postJson(keysUrl, { name: KEY_NAME }, auth, fetchImpl), "api key create") as
| Record<string, unknown>
| undefined);
const apiKey = trimmedString(keyRecord?.apiKey);
if (!apiKey) {
throw new AIError.OAuthError("Z.ai key provisioning returned no apiKey", {View on GitHub (pinned to 9690622007)
Solutions
- Log into the Z.ai console and create an organization and a project before running the login flow.
- Re-run the OAuth login after provisioning — the listing will then resolve org/project.
- If you are not an org admin, ask an admin to grant your account project membership.
- Inspect the raw org list response to rule out a schema change; update the library if Z.ai renamed fields.
Example fix
// before const biz = await mintZaiApiKey(oauthToken, fetch); // throws on fresh account // after await ensureZaiOrgAndProject(oauthToken); // creates org/project via console/API if missing const biz = await mintZaiApiKey(oauthToken, fetch);
Defensive patterns
Strategy: validation
Validate before calling
const orgs = await listZaiOrgs(token, fetch);
const org = orgs.find(o => o.isDefault) ?? orgs[0];
if (!org?.organizationId || !(org.projects ?? []).length) throw new Error("Z.ai account has no organization/project; create one in the console first"); Type guard
function hasProvisioning(org: unknown): org is { organizationId: string; projects: { projectId: string }[] } {
const o = org as { organizationId?: unknown; projects?: unknown };
return typeof o?.organizationId === "string" && o.organizationId.length > 0 && Array.isArray(o.projects) && o.projects.length > 0;
} Try / catch
try { return await mintZaiApiKey(token, fetch); }
catch (e) { if (e instanceof AIError.OAuthError && e.message.includes("no organization/project")) return guideUserToConsole(); throw e; } Prevention
- Provision an organization and project in the Z.ai console before automated login
- Ensure the OAuth user has project membership (not just org viewer)
- Detect fresh accounts and route them through a setup wizard
When it happens
Trigger: Org list returns empty array; the only org has no projects array; ids are present but empty/non-string so trimmedString returns undefined.
Common situations: Brand-new Z.ai account that never created an organization/project; enterprise account where project creation is restricted to admins; API returning a changed shape (projects nested differently).
Related errors
- Z.ai key provisioning returned no apiKey
- Z.ai key provisioning returned no secretKey
- failed to unmarshal LoadCodeAssistResponse: ${result.summary
- failed to unmarshal OnboardUser operation: ${result.summary}
- ${ineligibility.reasonMessage}${validation}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4eecf44e6585f83c.
Report an issue: GitHub.