Skip to content

OpenCode 1.4.11 (stock) returns "Forbidden: Invalid Token" against ucode-managed opencode.json — works against a Databricks-aware fork #98

@dgokeeffe

Description

@dgokeeffe

Summary

The same ucode-managed opencode.json succeeds against a Databricks-aware OpenCode fork but returns Forbidden: Invalid Token against published stock OpenCode 1.4.11. The token itself is valid (the fork accepts it on the same wire, with the same opencode.json, in the same shell).

This points at one of two things:

  1. Stock OpenCode strips or remaps the provider-level headers.Authorization that ucode writes, so the @ai-sdk/anthropic default x-api-key: <token> reaches the AI Gateway instead — and the gateway's Anthropic translator rejects x-api-key because Databricks bearer tokens are not Anthropic API keys.
  2. The fork carries header-handling code that stock OpenCode lacks (the fork adds an explicit Databricks provider that injects Authorization: Bearer <token> deterministically; stock OpenCode may run @ai-sdk/anthropic with its default header pipeline).

Either way, the practical outcome for an end user is the same: ucode configure --agents opencode succeeds, OpenCode launches, and every request 401s.

Reproduction

Using a workspace with AI Gateway v2 enabled (Claude path):

# 1. Configure ucode
DATABRICKS_CONFIG_PROFILE=<profile> \
  uv run ucode configure --agents opencode \
                          --workspaces https://<workspace>

# 2. Confirm the managed opencode.json carries the bearer token at the
#    provider level (not just at the model level):
jq '.provider["databricks-anthropic"].options' \
   ~/.config/ucode/opencode-xdg/opencode/opencode.json
# Expected fields: { baseURL, apiKey, headers: { Authorization: "Bearer ..." } }

# 3. Install stock OpenCode 1.4.11 from npm
bun add -g opencode-ai@1.4.11
which opencode    # /<bun-bin>/opencode

# 4. Launch via ucode against stock opencode
ucode opencode run "say hi in 5 words or less"

Observed (stock 1.4.11):

Forbidden: Invalid Token

OpenCode exits non-zero. Bun process logs show the AI Gateway returning HTTP 403 to the POST /ai-gateway/anthropic/v1/messages call.

Re-running the same command pointed at a Databricks-aware OpenCode fork (same managed config, same token, same workspace, same shell) succeeds:

OPENCODE_BIN_PATH=/path/to/fork/dist/opencode-darwin-arm64/bin/opencode \
  ucode opencode run "say hi in 5 words or less"
# -> "Hi! Five words for you."

The fork carries an explicit Databricks provider that wires Authorization: Bearer deterministically; stock OpenCode runs the AI SDK provider with its default header pipeline.

Hypothesis (most likely)

@ai-sdk/anthropic reads the apiKey option from the provider config and uses it to set x-api-key: <token> on every outbound request, regardless of any headers block. The AI Gateway's /ai-gateway/anthropic translator only accepts Authorization: Bearer <Databricks token> — it rejects x-api-key because that header carries an Anthropic-issued key.

ucode currently writes both:

"options": {
  "baseURL": "https://<ws>/ai-gateway/anthropic/v1",
  "apiKey": "<databricks-token>",
  "headers": { "Authorization": "Bearer <databricks-token>" }
}

…on the assumption that one of the two will land. In the fork the explicit Databricks provider takes precedence and only ships Authorization. In stock OpenCode the @ai-sdk/anthropic provider runs end-to-end and x-api-key wins.

Proposed fix candidates (smallest first)

Option A — drop apiKey, keep only Authorization header

In src/ucode/agents/opencode.py:98–106 and the equivalent gemini block at :109–117:

 providers["databricks-anthropic"] = {
     "npm": "@ai-sdk/anthropic",
     "options": {
         "baseURL": opencode_base_urls["anthropic"],
-        "apiKey": token,
+        # Don't pass apiKey: @ai-sdk/anthropic uses it to set x-api-key,
+        # which the Databricks AI Gateway rejects (it expects a Databricks
+        # bearer, not an Anthropic-issued API key). The Authorization header
+        # below is what the gateway actually authenticates against.
         "headers": auth_headers,
     },
     "models": dict.fromkeys(anthropic_models, anthropic_model_overlay),
 }

This needs verification — @ai-sdk/anthropic may refuse to construct without an apiKey. If so, pass a sentinel ("unused-databricks-uses-bearer") and add a one-line comment.

Option B — wait for a stock OpenCode release that exposes a Databricks provider natively

If Option A doesn't land on stock OpenCode for unrelated reasons (provider validation, header-overwriting upstream), the alternative is ucode pinning to a Databricks-aware OpenCode fork until the upstream merges native Databricks support. That's the path the fork-maintaining team is on today.

Ask

We want to retire the Databricks-aware OpenCode fork and rely on ucode + stock OpenCode. This Forbidden: Invalid Token is the second of two blockers (the first is the missing GPT-5 / Codex provider — filed separately). Could a ucode maintainer:

  1. Confirm whether the apiKey + headers.Authorization shape is intended to support stock OpenCode at all, and
  2. Either apply Option A above (and validate on opencode-ai@1.4.11) or document the OpenCode version range ucode actually targets in README.md?

Verification evidence

  • Token validity confirmed via direct curl against ${workspace}/ai-gateway/anthropic/v1/messages with Authorization: Bearer <token> — 200 OK.
  • Same opencode.json + same token + same shell → fork succeeds, stock 1.4.11 fails.
  • Codex-driven test run (timestamp 2026-05-25 22:38) captured the Invalid Token failure on opencode-ai@1.4.11; the local fork ran at commit cac7817a7 (feat/databricks-provider).

Metadata

Metadata

Assignees

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