Skip to content

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 #46354

Closed as not planned

Description

@ton618to

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 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):

  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

Activity

  1. github-actions commented on Aug 31, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. neriousy commented on Oct 22, 2026

    @neriousy
    Member

    Thanks for the detailed write-up. This was reported against V1 (1.18.23) and the code it points at, packages/opencode/src/provider/provider.ts and packages/opencode/src/auth/index.ts, is V1 code. V1 now only gets critical fixes, and this isn't one. If you'd like this in OpenCode 2.0, please open a new issue for 2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions