Skip to content

{env:VAR} substitution breaks auth.json fallback when env var is unset #34388

Closed as not planned

Description

@x93008

Description

When a provider's options.apiKey uses {env:VAR} syntax and the env var is not set, the {env:} substitution replaces it with an empty string "" (not undefined). This empty string then blocks the auth.json fallback at provider.ts:1686, causing opencode to send an empty/missing API key to the provider and fail with "Failed to initialize provider. Check credentials and configuration."

Expected behavior

When {env:VAR} resolves to empty (env var unset), opencode should fall back to credentials stored in auth.json (via /connect or opencode auth login) — since auth.json is the documented credential store and {env:} is documented as returning empty string when unset.

Actual behavior

The auth.json fallback is silently skipped. The SDK receives apiKey: "", sends no Authorization header (or sends Bearer ), and the API returns 401. The user sees: Failed to initialize provider "<id>". Check credentials and configuration.

Root cause

Two subsystems disagree on the semantics of "missing key":

  1. {env:} substitution (packages/opencode/src/config/variable.ts L36-37) returns "" for unset env vars:

    text.replace(/\{env:([^}]+)\}/g, (_, varName) => {
      return (input.env?.[varName] ?? process.env[varName]) || ""
    })

    Docs confirm: "If the environment variable is not set, it will be replaced with an empty string."

  2. auth.json fallback (packages/opencode/src/provider/provider.ts L1686) uses strict equality:

    if (options["apiKey"] === undefined && provider.key) options["apiKey"] = provider.key

    "" === undefined is false, so the fallback never fires.

Reproduction

  1. opencode auth login → Other → my-provider → paste a valid API key (stored in ~/.local/share/opencode/auth.json)
  2. Create opencode.jsonc:
    {
      "$schema": "https://opencode.ai/config.json",
      "provider": {
        "my-provider": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "baseURL": "https://api.example.com/v1",
            "apiKey": "{env:MY_API_KEY}"
          },
          "models": { "my-model": { "name": "My Model" } }
        }
      }
    }
  3. Do NOT set MY_API_KEY env var.
  4. opencode run "hello" — observe the error: Failed to initialize provider "my-provider". Check credentials and configuration.
  5. Expected: opencode falls back to the key in auth.json and succeeds.

Fix

packages/opencode/src/provider/provider.ts L1686 — change strict equality to falsy check:

- if (options["apiKey"] === undefined && provider.key) options["apiKey"] = provider.key
+ if (!options["apiKey"] && provider.key) options["apiKey"] = provider.key

This treats undefined, "", and null uniformly as "no key configured", allowing the auth.json fallback to take effect.

Environment

  • opencode: v1.17.11
  • OS: Linux

Activity

  1. github-actions commented on Jun 29, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of or closely related to existing issues. Please check:

    If your issue is distinct from the above (as it appears to be — specifically the interaction between empty-string substitution and the auth.json fallback), feel free to keep this open and note the distinction.

  2. x93008 commented on Jun 29, 2026

    @x93008
    Author

    Duplicate of #33853 — same root cause: ConfigVariable.substitute() in packages/opencode/src/config/variable.ts silently resolves unset {env:VAR} to empty string. #33853 covers the MCP auth scenario; this issue covers the provider apiKey scenario. Closing in favor of the earlier report.

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