goose + provider openai: the API key never reaches the agent (401 every turn)
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
Buzz stores the OpenAI key as `OPENAI_COMPAT_API_KEY` (buzz-agent's env contract, reused as the one secret field for every runtime).
goose's openai provider reads `OPENAI_API_KEY`.
Nothing translates between them, so the goose child starts with no credential it recognizes.
Readiness hides it: `readiness::goose_requirements` checks for `OPENAI_COMPAT_API_KEY` - the key Buzz itself wrote - so the agent shows as Ready with the key set, and the only symptom is a 401 at turn time.
## Evidence
A live goose child spawned by Buzz:
```
$ ps eww
GOOSE_PROVIDER=openai GOOSE_MODEL=gpt-5.6-sol
OPENAI_COMPAT_API_KEY=… # present
OPENAI_API_KEY # absent
```
And outside Buzz, with the same key:
```
OPENAI_COMPAT_API_KEY= goose run -t "say ok" # 401
OPENAI_API_KEY= goose run -t "say ok" # ok
```
## Affects
Any goose agent configured through Buzz with provider `openai`.
Users who stored the key via `goose configure` (system keychain) are unaffected, since goose falls back to the keyring.
The obvious workaround - typing `OPENAI_API_KEY` into the env-vars editor - is blocked: the config dialogs still demand the canonical name, so Save stays disabled.
## Proposed fix
Export the canonical value under `OPENAI_API_KEY` too when a goose child's effective provider is `openai` and the agent has no native key of its own.
Write it after the layered user env, and emit nothing when the user set `OPENAI_API_KEY` themselves, so an explicit value is never clobbered.
Resolve the provider the way the child does - layered env, then the structured field, then the inherited process env, then goose's `config.yaml` - since a later env layer can override `GOOSE_PROVIDER` in either direction.
## Not covered by that fix
- `openai-compat` stays broken for goose: it has no such provider, and reaches compatible endpoints via `openai` plus `OPENAI_HOST` / `OPENAI_BASE_PATH`. That needs Buzz's single `OPENAI_COMPAT_BASE_URL` split into an origin and a path, including a decision about URLs that already carry a version suffix.
- Accepting a user-typed `OPENAI_API_KEY` needs readiness *and* the dialogs changed together: `requiredCredentialEnvKeys` plus its four consumers, and `getGlobalAgentCredentialState`, which derives `apiKeyMissing` from the canonical key directly rather than from the required-keys list.
- The same class of bug exists wherever a harness's native env name differs from Buzz's canonical one. A per-runtime alias table is the durable shape.
Contributor guide
Research direction
Start at the goose child environment assembly and readiness::goose_requirements, then trace layered user env, structured provider fields, inherited process env, and goose's config.yaml in the provider-resolution path. Check requiredCredentialEnvKeys and getGlobalAgentCredentialState only to avoid changing the separate credential-dialog behavior. Done when an openai goose child receives the canonical key without overriding an explicit OPENAI_API_KEY, and readiness matches the effective credential.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100