-
Notifications
You must be signed in to change notification settings - Fork 12.2k
Description
Description
OPENCODE_CONFIG_CONTENT (inline config via environment variable) does not perform {env:VAR} or {file:path} token substitution. File-based config sources all go through loadFile() → load(), which applies regex-based substitution for both token types. The inline branch at line 179 of packages/opencode/src/config/config.ts calls JSON.parse() directly, skipping the load() pipeline entirely.
This means tokens like {env:MY_API_KEY} or {file:./secrets.txt} remain as literal strings in the parsed config when delivered via OPENCODE_CONFIG_CONTENT, but are correctly resolved when the same config is in a file. This is an inconsistency across config sources.
Steps to reproduce
-
Set an environment variable:
export MY_KEY="sk-test-12345"
-
Launch opencode with inline config containing an
{env:}token:OPENCODE_CONFIG_CONTENT='{"provider":{"my-provider":{"api_key":"{env:MY_KEY}"}}}' opencode -
Observe the provider's
api_keyis the literal string{env:MY_KEY}, notsk-test-12345. -
Place the identical JSON in
opencode.jsonand launch normally — the token resolves correctly.
Expected behavior: {env:MY_KEY} resolves to sk-test-12345 regardless of whether config is file-based or inline.
Actual behavior: Inline config retains the literal token string.
Root cause
In packages/opencode/src/config/config.ts:
- Line 179 — inline path uses raw
JSON.parse(Flag.OPENCODE_CONFIG_CONTENT), bypassing substitution. - Line 1233 —
loadFile()delegates toload(text, filepath)which performs{env:}(line 1238) and{file:}(line 1242) substitution before parsing.
Proposed fix direction
Route the inline config string through the existing load(text, configFilepath) function instead of raw JSON.parse. A synthetic configFilepath anchored to the project root (e.g. path.join(cwd, "<inline>")) would give {file:} tokens a sensible base directory for relative path resolution.
- result = mergeConfigConcatArrays(result, JSON.parse(Flag.OPENCODE_CONFIG_CONTENT))
+ result = mergeConfigConcatArrays(result, await load(Flag.OPENCODE_CONFIG_CONTENT, path.join(cwd, "<inline>")))Non-goals
This issue is specifically about token substitution parity. It does not propose any change to config source precedence (that was addressed separately in #11628).
Related issues
- OPENCODE_CONFIG_CONTENT does not have highest precedence config loading #11628 — config precedence fix (already merged)
{env:...}variable substitution inconsistently fails for specific MCP server URLs #5299, file substitution in configuration results in "invalid JSON" #1440, Config variables inopencode.jsonoverwritten with actual values on start up #9086, [FEATURE]: Auto-load .env files in scoped directories for {env:VAR} substitution #10458 — other substitution-related issues
OpenCode version
1.1.55