block / block/buzz

goose + provider openai: the API key never reaches the agent (401 every turn)

Open
#3,395 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.