Skip to content

Bug: OpenCode Go model names with dots get hyphenated, causing HTTP 401 (minimax-m2.7, glm-4.5, kimi-k2.5) #5211

@jacob3712

Description

@jacob3712

Summary

When using the opencode-go provider with the Hermes Agent CLI, any model whose name contains a dot as a version separator (e.g. minimax-m2.7, glm-4.5, kimi-k2.5) is silently mangled before the API request is made — dots are replaced with hyphens. This causes the OpenCode Go endpoint to return HTTP 401:

error type: ModelError
message: Model minimax-m2-7 not supported

The root cause is not the OpenCode Go API itself. Manual curl requests with the correct model name (minimax-m2.7) succeed. The bug lives entirely inside Hermes and affects two separate code paths.


Affected Models (non-exhaustive)

Model Mangled to Result
minimax-m2.7 minimax-m2-7 401 from OpenCode Go
minimax-m2.5 minimax-m2-5 401 from OpenCode Go
glm-4.5 glm-4-5 401 from OpenCode Go
kimi-k2.5 kimi-k2-5 401 from OpenCode Go
mimo-v2-omni mimo-v2-omni Works (no dot, coincidence)
glm-5 glm-5 Works (no dot, coincidence)

Models without a . version separator appeared to work not because the logic was correct, but because the mangled output happens to match the input.


Root Cause — Two Separate Bugs

Bug 1: hermes/cli/model_normalize.pyopencode-go incorrectly in DOT_TO_HYPHEN_PROVIDERS

opencode-go was included in DOT_TO_HYPHEN_PROVIDERS (alongside anthropic), causing normalize_model_for_provider() to call dots_to_hyphens() on all model names for this provider. The Anthropic API uses hyphens (e.g. claude-sonnet-4-6), but OpenCode Go uses its own identifiers with dots (e.g. minimax-m2.7).

Fix:

 DOT_TO_HYPHEN_PROVIDERS: frozenset[str] = frozenset({
     "anthropic",
     "opencode-zen",
-    "opencode-go",
 })

Bug 2: run_agent.pyanthropic_preserve_dots() missing opencode-go

When the API mode is anthropic_messages (which MiniMax models on the Go endpoint use), Hermes builds the request via anthropic_adapter.build_anthropic_kwargs(). That function calls normalize_model_name(model, preserve_dots=...). The preserve_dots argument is set by anthropic_preserve_dots(), which only checked for Alibaba DashScope — never for OpenCode Go. So even after Bug 1 is fixed, the API-layer call still converts minimax-m2.7minimax-m2-7.

Fix:

 def anthropic_preserve_dots(self) -> bool:
-    """True when using AlibabaDashScope anthropic-compatible endpoint —
-    model names keep dots, e.g. qwen3.5-plus."""
-    if getattr(self, "provider", "") or "").lower() == "alibaba":
+    """True when using an anthropic-compatible endpoint that preserves dots in model names.
+    AlibabaDashScope keeps dots (e.g. qwen3.5-plus).
+    OpenCode Go keeps dots (e.g. minimax-m2.7). Without this, dots become hyphens → 401."""
+    if getattr(self, "provider", "") or "").lower() in {"alibaba", "opencode-go"}:
         return True
     base = (getattr(self, "base_url", "") or "").lower()
-    return "dashscope" in base or "aliyuncs" in base
+    return "dashscope" in base or "aliyuncs" in base or "opencode.ai/zen/go" in base

Evidence

Request dump from a failing session (.hermes/sessions/request_dump.json):

{
  "method": "POST",
  "url": "https://opencode.ai/zen/go/chat/completions",
  "body": { "model": "minimax-m2-7" }  // dots already became hyphens here
}

Working curl with correct model name:

curl https://opencode.ai/zen/go/v1/messages \
  -H "x-api-key: sk-..." \
  -H "Content-Type: application/json" \
  -d '{"model": "minimax-m2.7", "max_tokens": 50, "messages": [{"role": "user", "content": "Say hello briefly"}]}'
# Returns a valid response

config.yaml is always correct — Hermes stores the name properly. The mangling happens after config is loaded, during the API request build step:

model_default: minimax-m2.7   # correct
provider: opencode-go
base_url: https://opencode.ai/zen/go/v1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions