Reproduced in the web dev app on Linux at 70e5e384da, using an assistant-message fixture in isolated state. I/W217:220 displays as W217 · L220; clicking opens the missing-file preview for <workspace>/I/W217. A/B12:40 and foo/TODO:12 also become chips; W217:W220 and E29:E31 remain plain code. No matching open duplicate found.
[Bug]: Inline code like `I/W217:220` renders as a file chip because a `:line` suffix skips the extension check#22181
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
The classifier lives in packages/shared/src/markdownLinks.ts.
Steps to reproduce
Open any thread.
Have an assistant response use spreadsheet cell shorthand in inline code, for example:
markdown1 The V5 file includes the `I/W217:220` fix (columns I and W, rows 217–220). Read the rendered message, then click the
I/W217:220span.
For a direct check on main @ b69703ccc240b9ecc6582be024f199d6d8dd44da:
| 1 | inlineCodeFilePathCandidate("I/W217:220"); // "I/W217:220" |
| 2 | inlineCodeFilePathCandidate("A/B12:40"); // "A/B12:40" |
| 3 | inlineCodeFilePathCandidate("W217:W220"); // null |
| 4 | inlineCodeFilePathCandidate("E29:E31"); // null |
Expected behavior
I/W217:220 renders as ordinary inline code with its full text. Nothing in the span looks like a file: there is no extension, the basename is not a known extensionless file name, and there is no ./, ~/, or absolute prefix.
Actual behavior
The span renders as a file chip labeled W217 · L220. The I/ prefix and the :220 row range are gone from the visible text, so the sentence no longer says what the agent wrote. Clicking the chip opens the file preview at <workspace>/I/W217, which does not exist:
| 1 | The file could not be accessed or read. It may be missing or inaccessible. |
| 2 | Attempted path <workspace>/I/W217 |
The path is resolved correctly against the workspace, which is on a remote server in my setup. The problem is only that the span should not be a link at all.
Cause
inlineCodeFilePathCandidate only applies the extension check when there is no :line suffix:
hasPositionis true for any trailing:\d+if (!hasPosition && !FILE_EXTENSION_PATTERN.test(basename)) return null;skips the extension check when a position is present.parseMarkdownFileLink→looksLikeFilePathaccepts it too, becauseRELATIVE_FILE_PATH_PATTERNaccepts any slash-separated token with:digitsand does not require an extension.fileLinkLabelthen shows only the basename plusL220.
Bare names already have a guard for this. TODO:12, error:1, and port:3000 stay plain code (markdown-links.test.ts "does not treat arbitrary name:digits shapes as files"). Adding one slash bypasses that guard: foo/TODO:12, I/W217:220, and A/B12:40 all become chips.
#13899 fixed the version-shaped basename case (z-ai/glm-5.3:12). This is the remaining case in the same branch: a slashed span with a position suffix and an extensionless basename.
Possible fix
For inline code without an explicit path prefix, require the basename to look like a file even when hasPosition is true. It should have a file extension or be in EXTENSIONLESS_FILE_NAMES, the same rule looksLikePosixFilesystemPath uses for absolute paths. Authored markdown links ([x](I/W217:220)) can keep linking as written.
If extensionless scripts like bin/run:12 need to stay chips, another option is to make a chip from an extensionless inline-code candidate only when the path exists in the workspace index. That index is already used for basename lookup.
Impact
Minor bug or occasional failure
This comes up whenever an agent talks about spreadsheets, grid coordinates, or any X/Y:N shorthand. The rendered text loses information, and the link goes to a file that does not exist.
Version or commit
Desktop 0.1.0 (Beta). Reproduced from source on main @ b69703ccc240b9ecc6582be024f199d6d8dd44da.
Environment
macOS desktop app connected to a T3 server on a second Mac. Provider: Claude. The classifier is provider-independent and runs on the client.