Repository navigation
Description
Description
Hello,
{env:VAR} variable substitution does not work for the apiKey field inside a custom provider's options when using @ai-sdk/openai-compatible. The API key is not passed to the provider, resulting in an Unauthorized / Not authenticated error.
However, {file:...} substitution works correctly for the same field, and hardcoding the API key also works. This suggests the {env:} substitution is either not applied or applied too late for custom provider options.
Environment
- OS: Linux (Ubuntu)
- OpenCode version: latest
- Shell: zsh
- Provider SDK:
@ai-sdk/openai-compatible
Steps to Reproduce
1. Set the environment variable
export MY_PROVIDER_API_KEY="sk-my-secret-key"
echo $MY_PROVIDER_API_KEY # confirms it's set2. Configure opencode.json with {env:...}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"my-provider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My Provider",
"options": {
"baseURL": "https://api.my-provider.example.com/v1",
"apiKey": "{env:MY_PROVIDER_API_KEY}"
},
"models": {
"my-model-large": {
"name": "my-model-large",
"limit": { "context": 131072, "output": 65536 }
}
}
}
}
}3. Run OpenCode and send any prompt
Unauthorized: {"detail":"Not authenticated"}
What works
| Method | Result |
|---|---|
"apiKey": "sk-my-secret-key" (hardcoded) |
✅ Works |
"apiKey": "{file:~/.secrets/my-provider-key}" |
✅ Works |
"apiKey": "{env:MY_PROVIDER_API_KEY}" |
❌ Fails — Unauthorized |
Verification
The API itself works fine — confirmed with curl using the same key:
curl -H "Authorization: Bearer $MY_PROVIDER_API_KEY" \
https://api.my-provider.example.com/v1/models
# Returns model list successfullyExpected Behavior
{env:MY_PROVIDER_API_KEY} should be substituted with the environment variable value before the API key is passed to the provider SDK, just like {file:...} is.
Actual Behavior
The {env:...} token appears to not be resolved (or resolved to an empty string) for custom provider options.apiKey. The request is sent without authentication.
Possibly Related Issues
{env:...}variable substitution inconsistently fails for specific MCP server URLs #5299 —{env:...}substitution inconsistently fails for MCP server URLs- Config variables in
opencode.jsonoverwritten with actual values on start up #9086 — Config variables overwritten with actual values on startup - OPENCODE_CONFIG_CONTENT bypasses {env:} and {file:} token substitution #13219 —
OPENCODE_CONFIG_CONTENTbypasses{env:}and{file:}substitution
Workaround
Use {file:...} substitution instead:
echo -n "$MY_PROVIDER_API_KEY" > ~/.secrets/my-provider-key
chmod 600 ~/.secrets/my-provider-key"apiKey": "{file:~/.secrets/my-provider-key}"Plugins
No response
OpenCode version
No response
Steps to reproduce
No response
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response
Activity
github-actions commentedon Mar 29, 2026
This issue might be a duplicate of existing issues. Please check:
{env:VARIABLE_NAME}not working as one of the attempted solutions, and covers the broader problem of options (includingapiKey) not being forwarded to the provider SDK.arz03 commentedon Apr 2, 2026
i am facing the same issue. is there any timeline or fixes planned for this issue?
@rekram1-node
proweb commentedon May 9, 2026
same try to get my provider API KEY from .env in project folder and opencode don't got it
VooDisss commentedon May 10, 2026
Same here, so I have created a small workaround python script for that - you can put it into your global opencode directory and it will create a child folder "api_keys" and it will look into opencode.json for all the keys and will then look into the .env file that references them and will create individual files with those mapped keys into the api_keys directory...
I have tried to find which version of opencode introduced this issue, but I was not able to! I have tried to go back as far as
opencode upgrade 1.3.0! Then did uninstall and install vianpm uninstall -g opencode-ai@latestandnpm i -g opencode-ai@latestand just settled for this script as a workaround...Details
``` import json import os import re import shutil from pathlib import PathSCRIPT_DIR = Path(file).parent.resolve()
OPENCODE_JSON = SCRIPT_DIR / "opencode.json"
ENV_FILE = SCRIPT_DIR / ".env"
API_KEYS_DIR = SCRIPT_DIR / "api_keys"
ENV_PATTERN = re.compile(r'{env:([A-Z_][A-Z0-9_]*)}', re.IGNORECASE)
def discover_env_vars(config: dict, parent_path: str = "") -> list[str]:
vars_found = []
for key, value in config.items():
path = f"{parent_path}.{key}" if parent_path else key
if isinstance(value, str):
for match in ENV_PATTERN.finditer(value):
vars_found.append(match.group(1))
elif isinstance(value, dict):
vars_found.extend(discover_env_vars(value, path))
return vars_found
def load_env_file(env_path: Path) -> dict[str, str]:
env_vars = {}
if not env_path.exists():
print(f"[WARN] .env not found at {env_path}")
return env_vars
for line in env_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
k, v = line.split("=", 1)
k = k.strip()
v = v.strip().strip('"').strip("'")
env_vars[k] = v
return env_vars
KEY_NAME_PATTERNS = ["API_KEY", "SECRET", "TOKEN", "PASSWORD", "_KEY", "PERSONAL_ACCESS_TOKEN"]
def looks_like_api_key(name: str) -> bool:
name = name.upper()
return any(p.upper() in name for p in KEY_NAME_PATTERNS)
def patch_config(config: dict, updated_vars: list[str]):
count = 0
for key, value in config.items():
if isinstance(value, str):
for var in updated_vars:
if f"{{env:{var}}}" in value:
config[key] = value.replace(f"{{env:{var}}}", f"{{file:api_keys/{var}}}")
count += 1
break
elif isinstance(value, dict):
count += patch_config(value, updated_vars)
return count
def main():
print(f"[sync-env-keys] Folder: {SCRIPT_DIR}")
print()
if name == "main":
main()
disonjer commentedon May 14, 2026
When is the fix?
opencode-agent commentedon Jul 2, 2026
I tried to reproduce this on current
dev/v1.17.13and couldn't reproduce it.Repro I checked:
@ai-sdk/openai-compatibleoptions.apiKeyset to"{env:CUSTOM_PROVIDER_API_KEY}"CUSTOM_PROVIDER_API_KEY=env-custom-keypresent in the OpenCode process env before provider/config loadThe SDK received the substituted key correctly:
Targeted test command I ran locally after adding the temporary regression:
That passed, and the current config loader applies
{env:...}substitution before JSONC parsing, sooptions.apiKeyshould already be a concrete string by the time the provider SDK is created.One likely source of confusion: OpenCode doesn't automatically load a project
.envfile for config substitution. The variable needs to exist in the actual OpenCode process environment. If OpenCode is launched from a desktop app, service, different shell, or any process that didn't inheritMY_PROVIDER_API_KEY,{env:MY_PROVIDER_API_KEY}resolves empty and the provider will authenticate as if no key was set.Closing this as stale / not reproducible on the current version. If someone can still reproduce on a current release, please open a fresh issue with the full provider config shape, OpenCode version, launch method, and whether the variable is visible in the OpenCode process env.