anomalyco / anomalyco/opencode

bug(provider): credentials set via CLI/`/connect` are not applied to a running server (SAP AI Core, GitHub Copilot), and SAP multi-line keys are stored truncated

Open
#46,354 1 comment 0 reactions 1 assignee View on GitHub

@nexxeln is already working on this.

Since Aug 31, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

bug(provider): credentials set via CLI//connect are not applied to a running server (SAP AI Core, GitHub Copilot), and SAP multi-line keys are stored truncated

Summary

Configuring a provider on an already-running server is unreliable. Three related bugs, two SAP-specific plus one shared with GitHub Copilot:

  • Bug A (SAP) — TUI /connect truncates a multi-line pasted credential to its first line, so a pretty-printed SAP service-key JSON is stored as just {.
  • Bug B (SAP) — the SAP loader copies the key into process.env.AICORE_SERVICE_KEY once and never re-reads it; Auth.set never clears it, so a poisoned value survives for the whole process lifetime.
  • Bug C (shared: SAP, GitHub Copilot) — a running server caches provider/credential state and does not invalidate it when auth.json changes out-of-band (e.g. via opencode auth login in a separate process, or another instance). Credentials appear "connected" on disk but the running server keeps failing until a full restart.

Net effect: opencode auth login (CLI) succeeds, but the running TUI/server keeps failing; only restarting the process picks up the credential.

Observed SAP error:

AI_LoadAPIKeyError: SAP AI Core authentication failed: Could not find service credentials for AI Core. Please check the service binding.
Make sure your AICORE_SERVICE_KEY environment variable is set correctly.

Bug A — multi-line paste truncated to first line (SAP)

The SAP service key is a multi-line JSON object (clientid, clientsecret, url, serviceurls.AI_API_URL). Pasting it into the TUI /connect API-key prompt stores only {.

Cause: the API-key prompt is DialogPrompt, whose submit command is bound to return (packages/tui/src/ui/dialog-prompt.tsx). A pasted multi-line value submits at the first newline, so only { is captured and written to auth.json. opencode auth login (clack CLI prompt) captures the full value — hence the confusing CLI-works / /connect-fails asymmetry.

On disk after /connect:

{ "sap-ai-core": { "type": "api", "key": "{" } }

Bug B — AICORE_SERVICE_KEY cached in process.env and never refreshed (SAP)

packages/opencode/src/provider/provider.ts (origin/dev):

"sap-ai-core": Effect.fnUntraced(function* () {
  const auth = yield* dep.auth("sap-ai-core")
  const envServiceKey = iife(() => {
    const envAICoreServiceKey = process.env.AICORE_SERVICE_KEY
    if (envAICoreServiceKey) return envAICoreServiceKey   // stale value wins forever
    if (auth?.type === "api") {
      process.env.AICORE_SERVICE_KEY = auth.key           // set once, sticky for process lifetime
      return auth.key
    }
    return undefined
  })
  ...
})

Once a request runs with a bad key (e.g. the { from Bug A), process.env.AICORE_SERVICE_KEY is poisoned for the whole process. Auth.set (packages/opencode/src/auth/index.ts) only writes auth.json; it never clears the env var, and the loader short-circuits on process.env before consulting auth.json again. amazon-bedrock has the same pattern with AWS_BEARER_TOKEN_BEDROCK (provider.ts ~lines 320-326).

Bug C — running server does not pick up out-of-band credential changes (SAP + GitHub Copilot)

The provider registry is built once into cached instance state. Auth.set / auth.json has no watcher that invalidates it, so credentials written by a separate processopencode auth login, hand-editing auth.json, or a second instance — are not seen by an already-running server. In-process /connect recovers via instance.dispose() for the OAuth path (packages/tui/src/component/dialog-provider.tsx), but out-of-band writes require a full restart.

Reported symptom for both SAP AI Core and GitHub Copilot: the credential is present and valid in auth.json (for Copilot, a complete oauth entry with refresh/access), yet the running server keeps using stale state until restarted.

Reproduction

SAP (Bugs A + B):

  1. /connect → SAP AI Core → paste the multi-line service-key JSON.
  2. Send a message to a sap-ai-core model → Could not find service credentials.
  3. Fix auth.json (or opencode auth login) so the key is valid single-line JSON.
  4. Retry in the same running server → still fails.
  5. Restart the process → works.

Shared (Bug C, SAP or GitHub Copilot):

  1. With a server already running, run opencode auth login in a separate shell and connect the provider.
  2. Back in the running server, use the provider → still fails / not available.
  3. Restart the server → works.

Suggested fixes

  • Bug A: normalize/validate the SAP key at the storage boundary — in Auth.set, when the key is sap-ai-core and info.type === "api", run JSON.stringify(JSON.parse(info.key)) and reject invalid JSON; this compacts pretty-printed input and rejects the truncated { instead of silently persisting it. Ideally also make the TUI prompt not submit on the newline of a bracketed paste.
  • Bug B: on Auth.set("sap-ai-core", …) (and amazon-bedrock), clear the corresponding process.env var, or make the loader treat auth.json as the source of truth rather than caching into process.env.
  • Bug C: invalidate/rebuild cached provider state when auth.json changes (watch the file, or re-read auth on each provider resolution), so out-of-band auth login is applied without a restart.

Environment

  • opencode dev branch (package version 1.18.23)
  • macOS (Apple Silicon), Bun runtime
  • Providers: sap-ai-core (@jerome-benoit/sap-ai-provider-v2), github-copilot

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.