Skip to content

Z.ai static catalog models resolve without baseUrl and fall through to OpenAI API #94269

Description

@chrysb

Summary

Selecting a native Z.ai model such as zai/glm-5-turbo can produce an OpenAI 401 at runtime even though OpenClaw logs the run as provider=zai model=glm-5-turbo. The resolved runtime model is missing the Z.ai baseUrl, so the OpenAI-compatible transport constructs the OpenAI SDK client without a base URL and appears to fall back to OpenAI's default API host.

Observed behavior

A user reported:

  • AlphaClaw UI/config changed primary from anthropic/claude-opus-4-6 to zai/glm-5-turbo.
  • Gateway startup says: agent model: zai/glm-5-turbo.
  • Failed run says provider=zai model=glm-5-turbo.
  • The 401 text is OpenAI's own error: Incorrect API key provided... platform.openai.com/account/api-keys.

This is consistent with OpenAI SDK default-host fallback, not with a request going to https://api.z.ai/....

Reproduction

Tested against both:

import { resolveModel } from "./node_modules/openclaw/dist/model-CNcUE2JX.js";

const cfg = {
  agents: {
    defaults: {
      model: { primary: "zai/glm-5-turbo" },
      models: { "zai/glm-5-turbo": {} },
    },
  },
};

const result = resolveModel("zai", "glm-5-turbo", "/tmp/openclaw-agent-test", cfg);
console.log(JSON.stringify({
  error: result.error,
  model: result.model && {
    provider: result.model.provider,
    id: result.model.id,
    api: result.model.api,
    baseUrl: result.model.baseUrl,
  },
}, null, 2));

Actual output on 2026.6.8:

{
  "model": {
    "provider": "zai",
    "id": "glm-5-turbo",
    "api": "openai-completions"
  }
}

Expected behavior

The resolved runtime model should include the manifest provider base URL:

{
  "provider": "zai",
  "id": "glm-5-turbo",
  "api": "openai-completions",
  "baseUrl": "https://api.z.ai/api/paas/v4"
}

Notes

The bundled Z.ai manifest/catalog does include the provider-level base URL and API:

{
  "providers": {
    "zai": {
      "baseUrl": "https://api.z.ai/api/paas/v4",
      "api": "openai-completions"
    }
  }
}

Adding explicit provider config works around the issue:

"models": {
  "providers": {
    "zai": {
      "baseUrl": "https://api.z.ai/api/paas/v4",
      "api": "openai-completions"
    }
  }
}

So this looks like the static manifest model resolution path is returning the model row without inheriting provider-level transport fields such as baseUrl.

Impact

Users can successfully switch primary/default model to zai/glm-5-turbo, and status/logging shows the selected Z.ai provider/model, but runtime requests may authenticate against OpenAI instead of Z.ai, causing confusing OpenAI 401 errors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions