Skip to content

bug(v2): SAP AI Core unusable with valid saved credentials — apiKey/serviceKey mismatch blocks authentication #56126

Open

Description

@ton618to

Description

Blocking V1 → V2 regression: SAP AI Core is unusable through the normal /connect / saved-credential workflow. A valid service key works in V1, but after entering the same key in compiled V2, model requests fail because the key never reaches the SAP SDK.

This is not an invalid-token problem or a cosmetic error. For users relying on saved credentials, with no AICORE_SERVICE_KEY or service binding configured, SAP AI Core cannot authenticate any model request. The authentication message sends users toward debugging or replacing credentials that are already valid. Please prioritize this as a provider-blocking regression.

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.

Root cause

The V2 credential producer and consumer disagree on the field name:

  • packages/core/src/model-resolver.ts, nativeCredentialSettings: a saved key becomes { apiKey: credential.key }.
  • packages/core/src/aisdk.ts: forwards these settings into SDK options.
  • SapAICorePlugin: reads only evt.options.serviceKey or process.env.AICORE_SERVICE_KEY.

Consequently, the saved key exists but the SAP SDK sees no service credentials. The upstream dev SAP hook still contained this mismatch when checked.

Steps to reproduce

  1. Use a SAP AI Core service-key JSON that works in V1.
  2. Start a fresh compiled V2 server without AICORE_SERVICE_KEY or VCAP_SERVICES.
  3. Save the complete key through /connect → SAP AI Core. The corresponding API is POST /api/integration/sap-ai-core/connect/key with { "key": "<service-key JSON>" }.
  4. Select a SAP model and send a short prompt.
  5. The request fails with missing-service-credentials authentication errors despite the valid saved key.

Expected: the documented saved-credential workflow must authenticate without requiring a second manual environment-variable configuration.

Verified differential reproduction

An isolated probe traversed the real ModelResolver.fromCatalogModel → AISDK → SapAICorePlugin → SAP SDK doStream path with the same key, provider package, model (gpt-4.1-mini), and prompt:

Credential path Result
Saved key, before correction Resolver passes apiKey; SAP SDK credential absent; authentication fails
Same key through AICORE_SERVICE_KEY Real SAP request succeeds: OK.
Saved key after changing the hook to read apiKey Real SAP request succeeds: OK

The local correction changes evt.options.serviceKey to evt.options.apiKey, retaining existing explicit environment precedence. Three focused regression tests and core typechecking pass.

A patched compiled-server check also saved the key through the connect API (204) and got past authentication, but then hit a separate LLMEvent.stepFinish schema failure in aisdk.ts when consuming the SAP provider's V2 finish event. This is not a claim that the one-line credential correction makes the entire SAP conversation pipeline healthy; end-to-end verification must also account for that subsequent compatibility blocker.

Related reports / scope

Plugins

No user plugins required.

OpenCode version

V2 local checkout with @opencode/core / CLI version 2.0.31; credential-path reproduction and locally compiled verification. This is not a claim that the diagnostic binary is an official published release.

Bun 1.4.2; @jerome-benoit/sap-ai-provider-v2 5.0.3.

Operating System

macOS, Darwin 27.0.0, arm64.

Terminal

Not terminal-specific; verified through isolated SDK and headless HTTP paths.

Activity

  1. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I reproduced this on v2.0.31, which is currently the latest release.

    What I did:

    • Started opencode serve with no AICORE_SERVICE_KEY or VCAP_SERVICES set.
    • Saved a service key through POST /api/integration/sap-ai-core/connect/key, which returned 204. The key pointed at a local mock of the SAP endpoints.
    • Ran opencode run -m sap-ai-core/gpt-4.1-mini "say OK". It failed with SAP AI Core authentication failed: Could not find service credentials for AI Core…, and the mock received no requests, so the saved key was never used.
    • With the same key exported as AICORE_SERVICE_KEY, the SDK did request a token from the mock.

    This matches the mismatch you describe: the resolver passes the saved key as apiKey, but the SAP hook reads only serviceKey or the env var. The hook is still the same on the latest dev (582c974). Running dev from source fails earlier with the init crash from #54791, so I could only confirm this bug on the release. The script below reproduces it.

    For now, exporting the key as AICORE_SERVICE_KEY gets past authentication.

    Related:

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Repro for #56126: SAP AI Core key saved via /connect never reaches the SAP SDK.
    # Usage: repro.sh            -> uses `opencode` from PATH
    #        OC_DEV=/path/to/repo repro.sh -> runs from source checkout
    set -u
    cd "$(dirname "$0")"
    OC=(opencode)
    [ -n "${OC_DEV:-}" ] && OC=(bun --conditions=browser --preload=$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js $OC_DEV/packages/cli/src/index.ts)
    export HOME=$(mktemp -d) XDG_CONFIG_HOME= XDG_DATA_HOME= XDG_STATE_HOME= XDG_CACHE_HOME=
    unset AICORE_SERVICE_KEY VCAP_SERVICES
    # CONTROL=1: also export the same key as AICORE_SERVICE_KEY; the mock then receives the SDK's token request
    [ -n "${CONTROL:-}" ] && export AICORE_SERVICE_KEY='{"clientid":"cid","clientsecret":"sec","url":"http://127.0.0.1:9999","serviceurls":{"AI_API_URL":"http://127.0.0.1:9999"},"identityzone":"x","identityzoneid":"x","appname":"x"}'
    export OPENCODE_PASSWORD=pw
    mkdir -p $HOME/proj && cd $HOME/proj && git init -q
    bun "$OLDPWD/mock.ts" > $HOME/mock.log 2>&1 & MOCK=$!
    "${OC[@]}" serve --port 4199 --hostname 127.0.0.1 --print-logs > $HOME/serve.log 2>&1 & SRV=$!
    for i in $(seq 60); do curl -s -o /dev/null http://127.0.0.1:4199/ && break; sleep 1; done
    for i in $(seq 60); do curl -s -u opencode:pw http://127.0.0.1:4199/api/integration | grep -q sap-ai-core && break; sleep 1; done
    KEY='{"clientid":"cid","clientsecret":"sec","url":"http://127.0.0.1:9999","serviceurls":{"AI_API_URL":"http://127.0.0.1:9999"},"identityzone":"x","identityzoneid":"x","appname":"x"}'
    echo "== save key via connect API"
    curl -s -u opencode:pw -w ' HTTP %{http_code}\n' -X POST http://127.0.0.1:4199/api/integration/sap-ai-core/connect/key \
      -H 'content-type: application/json' -d "$(jq -n --arg k "$KEY" '{key:$k}')"
    echo "== run prompt"
    timeout 120 "${OC[@]}" run --server http://127.0.0.1:4199 -m sap-ai-core/gpt-4.1-mini "say OK" 2>&1 | tail -20
    echo "== mock requests (saved key used => oauth/token request appears)"; cat $HOME/mock.log
    echo "== server log matches"; grep -iE "service credentials|AICORE|sap" $HOME/serve.log | tail -10
    kill $SRV $MOCK 2>/dev/null

    Run it: bash repro.sh

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions