Skip to content

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

Description

@filipkujawa

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 <pid>
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=<key> goose run -t "say ok"   # 401
OPENAI_API_KEY=<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.

Metadata

Metadata

Assignees

No one assigned

    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