Skip to content

[Bug]: File links in agent output always resolve against the thread's project, not the repo the file is in #10553

Description

@spaansba

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

  1. Register two projects in the same environment, for example /repos/app-a and /repos/app-b.
  2. Open a thread in the app-a project.
  3. Ask the agent about code in the other checkout, so it runs something like cd ../app-b && cat src/api/rules/categories.ts.
  4. In its answer the agent refers to the file the way it read it, relative to where it was standing: `rules/categories.ts:7` or [categories.ts](rules/categories.ts).
  5. Click the resulting file chip, or open its context menu and pick "Copy full path".

Expected behavior

The chip points at the file the agent actually read, in /repos/app-b.

Actual behavior

Every relative reference is joined onto the thread's own project root, so the chip resolves to /repos/app-a/rules/categories.ts. That file does not exist. Clicking opens the wrong path, and "Copy full path" copies it.

This happens for both markdown links and auto-linked inline code spans. It is the same for any agent that steps outside the thread's workspace during a turn.

Impact

Minor bug or occasional failure

Version or commit

main @ latest

Environment

macOS, web client (also applies to desktop, which wraps web). Mobile does not render these chips.

Logs or stack traces

N/A

Workaround

Ask the agent for absolute paths. Absolute references link correctly.

Notes

resolveMarkdownFileLinkTarget in apps/web/src/markdown-links.ts ends with resolvePathLinkTarget(pathWithPosition, cwd), where cwd is always the thread's project root. Nothing downstream checks whether the resulting path exists, so a relative reference from another checkout silently produces a link into the wrong repo.

The prose itself carries no repo, and the agent's real working directory came from a cd inside a Bash call, which is not tracked. Resolving this properly seems to need an existence check against the environment's other project roots, most likely at click time so chat rendering stays free.

Activity

  1. juliusmarminge commented on Sep 7, 2026

    @juliusmarminge
    Member

    Triage

    Confirmed. Relative file chips in agent markdown are always joined onto the thread project (or its worktree). Nothing checks that the result exists, and nothing tries the environment’s other project roots. Absolute paths already work.

    What the code does

    ChatView passes markdownCwd={gitCwd} (the thread’s workspaceRoot / worktree) into ChatMarkdown. Both markdown links and auto-linked inline code go through resolveMarkdownFileLinkTarget → resolvePathLinkTarget, which joins any relative path onto that cwd. The chip tooltip and Copy full path use that already-resolved targetPath.

    The existing click-time workspace-index rescue (findWorkspaceBasenameMatch / needsWorkspaceBasenameLookup) only runs for slash-free names (categories.ts, not rules/categories.ts), and projects.searchEntries is scoped to that cwd. A cd inside Bash is not tracked.

    Related, not a duplicate

    Suggested fix

    Keep chat rendering cheap. At action time (open, copy full path, reveal, tooltip):

    1. Try the thread project join first.
    2. If that path does not exist, probe other project roots in the same environment (useProjects() is already available in ChatMarkdown).
    3. One unique hit → use it. Ambiguous / none → keep the current join (or offer a picker).

    Do not try to reconstruct Bash cd in the orchestrator. Prefer a cheap exists/stat over projects.readFile. Watch the case where the thread project happens to have a file at the same relative path — today’s join would still win and stay wrong.

    Workaround still holds: ask the agent for absolute paths.

    Labels: bug, via-triage (remove needs-triage)

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Sep 7, 2026
  3. git-pi-e commented on Sep 29, 2026

    @git-pi-e

    Claude Code-specific request: please have T3 automatically pick up and display the active Claude working directory in the thread, similar to the cwd / workspace.current_dir fields Claude Code provides to status lines. The UI should distinguish that current directory from the original project directory.

    When a reply contains a relative file link or file chip, Open, Copy full path, and Reveal should use and show the directory that actually makes the reference valid. Please do not silently resolve every link against the thread's original project root. If more than one known root contains the same relative path, let the user choose the target.

    A displayed session CWD alone may not identify a file inspected through a temporary cd inside one Bash call. Where T3 has the originating tool's CWD, use that for the link. This is the cross-repository case here. #8251 / #8254 cover the related case within one workspace.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions