Skip to content

Conversation

@conradkoh
Copy link

Summary

Fixes a bug where {file:path} template expansions fail when the same file path appears earlier in a commented line.

Closes #5731

Root Cause

The config loader performs string-based template expansion before JSON parsing. When multiple instances of the same {file:path} exist in the config file, findIndex always returns the index of the first occurrence. If the first occurrence is in a commented line, the loader skips all subsequent (valid) instances of that same file reference.

Example of the Bug

{
  "agent": {
    // "experimental": {
    //   "prompt": "{file:./prompts/precise.md}"  // ← findIndex finds this first
    // },
    "precise": {
      "prompt": "{file:./prompts/precise.md}"  // ← This never gets expanded
    }
  }
}

Location

packages/opencode/src/config/config.ts, the load function

Problematic Code

for (const match of fileMatches) {
  const lineIndex = lines.findIndex((line) => line.includes(match))  // BUG: Always finds FIRST occurrence
  if (lineIndex !== -1 && lines[lineIndex].trim().startsWith("//")) {
    continue // Skip if line is commented
  }
  // ... file reading and replacement ...
}

Solution

This PR includes two commits:

1. Minimal Fix (Commit 1)

  • Track all match positions and line numbers upfront
  • Process matches in reverse order to preserve string indices
  • Check each specific occurrence's line for comments

This ensures that commented-out file references don't prevent valid references from being expanded.

2. Parse-First Refactor (Commit 2)

Refactors the architecture to be more robust and maintainable:

  • Parse JSONC first, then expand templates in the object tree
  • Recursive tree walker for template expansion
  • Eliminates fragile comment detection and string injection risks

Benefits:

  • JSONC parser handles comments correctly (no manual checking needed)
  • No string injection vulnerabilities
  • Direct value replacement is safer than string manipulation
  • Template expansion is context-aware
  • Cleaner separation of concerns

Testing

  • ✅ Added reproduction test case demonstrating the shadowing bug
  • ✅ Added test for multiple agents with different file references
  • ✅ All 21 config tests pass
  • ✅ Full test suite: 305 pass (1 pre-existing failure unrelated to changes)
  • ✅ TypeScript type checks pass

Changes

  • packages/opencode/src/config/config.ts: Config loading implementation
  • packages/opencode/test/config/config.test.ts: New test cases

Architectural Issues Resolved

The original approach had several problems:

  1. Comment Detection is Fragile: Manual check using startsWith("//") doesn't understand JSON comment rules
  2. Duplicate Path Handling: Can't distinguish between multiple instances of the same file path
  3. Injection Vulnerability: File contents are injected into raw JSON string using JSON.stringify().slice(1, -1)
  4. No Context Awareness: Template expansion happens without understanding the JSON structure

The new parse-first architecture resolves all of these issues.

Related

…comments

Fixes a bug where {file:path} template expansions fail when the same
file path appears earlier in a commented line. The issue was caused by
findIndex() always returning the first occurrence of a match,
regardless of which specific occurrence was being processed.

Changed the logic to:
1. Track all match positions and their line numbers upfront
2. Process matches in reverse order to preserve string indices
3. Check each specific occurrence's line for comments

This ensures that commented-out file references don't prevent valid
references from being expanded.

Adds reproduction test case demonstrating the shadowing bug.
Refactors the config loading system to parse JSONC first, then expand
{env:VAR} and {file:path} templates within the resulting object tree.

Benefits of this approach:
- JSONC parser correctly handles comments (no manual checking needed)
- No string injection vulnerabilities
- Direct value replacement is safer than string manipulation
- Template expansion is context-aware
- Cleaner separation of concerns

The new architecture:
1. Parse JSONC text into JavaScript object
2. Walk the object tree recursively
3. Expand templates in string values only

This eliminates the fragile comment detection logic and makes the
template expansion more robust and maintainable.
@rekram1-node
Copy link
Collaborator

/review

@conradkoh
Copy link
Author

There are 3 distinct commits - it might be a change bigger than wanted.

  1. Just fix the issue
  2. Rework replacement to do config parsing first then replacement (previous was replacement in config then parsing)
  3. Performance optimizations for walking the json

The first commit should be a standalone fix and should be working as is. If the change is too big, can discard the other 2 commits.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@rekram1-node
Copy link
Collaborator

Did ur agent write that comment? Haha

@conradkoh
Copy link
Author

haha nope it didn't! I wrote it! it's my first time contributing here -- I came across some articles talking about the conservative approach to contributions! wanted to give a head's up so that it's easier to review!

@conradkoh
Copy link
Author

conradkoh commented Dec 19, 2025

but the PR is description and code are 100% agent written 😬

@conradkoh conradkoh force-pushed the fix-agent-prompt-loading-issue-5731 branch from 03e3b91 to 31b5834 Compare December 19, 2025 04:15
@rekram1-node
Copy link
Collaborator

Haha sounds good

@conradkoh conradkoh force-pushed the fix-agent-prompt-loading-issue-5731 branch from bd8830c to f98c605 Compare December 19, 2025 06:06
@conradkoh
Copy link
Author

@rekram1-node updated the code according with the generate command ran.

@conradkoh
Copy link
Author

hey @rekram1-node just wanted to check - is there anything else needed from me to get this merged? just making sure that it isn't getting stalled by something I should have done but didn't do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent file prompts not loading for some file names

2 participants