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
@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
/connecttruncates 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_KEYonce and never re-reads it;Auth.setnever 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.jsonchanges out-of-band (e.g. viaopencode auth loginin 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 process — opencode 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):
/connect→ SAP AI Core → paste the multi-line service-key JSON.- Send a message to a
sap-ai-coremodel →Could not find service credentials. - Fix
auth.json(oropencode auth login) so the key is valid single-line JSON. - Retry in the same running server → still fails.
- Restart the process → works.
Shared (Bug C, SAP or GitHub Copilot):
- With a server already running, run
opencode auth loginin a separate shell and connect the provider. - Back in the running server, use the provider → still fails / not available.
- Restart the server → works.
Suggested fixes
- Bug A: normalize/validate the SAP key at the storage boundary — in
Auth.set, when the key issap-ai-coreandinfo.type === "api", runJSON.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", …)(andamazon-bedrock), clear the correspondingprocess.envvar, or make the loader treatauth.jsonas the source of truth rather than caching intoprocess.env. - Bug C: invalidate/rebuild cached provider state when
auth.jsonchanges (watch the file, or re-read auth on each provider resolution), so out-of-bandauth loginis 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.