BUG-HUNT: [boundary] LspRuntime._path_to_uri() and LspClient.initialize() produce invalid file URIs for paths with spaces or special characters — LSP server rejects or mislocates files #6583

Open
opened 2026-04-09 21:47:04 +00:00 by HAL9000 · 0 comments
Owner

Bug Report: [boundary] — Missing percent-encoding in file:// URI construction

Severity Assessment

  • Impact: Any file path or workspace path containing spaces, hash signs, percent signs, or other URI-reserved characters produces an invalid file:// URI. LSP servers that strictly validate URIs will reject the request or correlate diagnostics to the wrong URI. This is a silent correctness bug — the runtime returns no error, but diagnostics/completions/hover results are silently lost or misattributed.
  • Likelihood: Medium — workspace paths with spaces (/home/user/My Project) are common on Windows and macOS developer machines, and are not unusual on Linux.
  • Priority: Medium

Location

  • File: src/cleveragents/lsp/runtime.py
    • Function: LspRuntime._path_to_uri() — lines 366–370
  • File: src/cleveragents/lsp/client.py
    • Function: LspClient.initialize() — lines 214–217

Description

Both _path_to_uri and initialize construct file:// URIs by simple string concatenation, without percent-encoding the path components:

# runtime.py line 370
return f"file://{file_path}"

# client.py line 217
workspace_uri = f"file://{workspace_path}"

Per RFC 3986 and the LSP specification, file:// URIs must be percent-encoded. For example:

  • /home/user/my project/main.pyfile:///home/user/my%20project/main.py
  • /tmp/work#1/file.pyfile:///tmp/work%231/file.py

Without encoding, the URI file:///home/user/my project/main.py has an unencoded space, which is syntactically invalid. LSP servers (e.g., pyright, rust-analyzer) reject or misparse such URIs. The result is that:

  1. textDocument/publishDiagnostics notifications from the server reference the correct percent-encoded URI.
  2. client.get_diagnostics(uri) looks up self._diagnostics[uri] using the unencoded URI.
  3. The lookup misses because the server stored diagnostics under the encoded URI.
  4. get_diagnostics silently returns an empty list.

Evidence

# runtime.py lines 365-370
@staticmethod
def _path_to_uri(file_path: str) -> str:
    """Convert a filesystem path to a ``file://`` URI."""
    if file_path.startswith("file://"):
        return file_path
    return f"file://{file_path}"    # ← no percent-encoding!
# client.py lines 214-217
workspace_uri = workspace_path
if not workspace_uri.startswith("file://"):
    workspace_uri = f"file://{workspace_path}"   # ← no percent-encoding!

The Python standard library provides pathlib.Path.as_uri() (Python 3.4+) which correctly encodes path components, or urllib.parse.urlencode for manual construction.

Expected Behavior

_path_to_uri("/home/user/my project/main.py") should return "file:///home/user/my%20project/main.py".

Actual Behavior

_path_to_uri("/home/user/my project/main.py") returns "file:///home/user/my project/main.py" — an invalid URI with an unencoded space.

Suggested Fix

Use pathlib.Path.as_uri():

from pathlib import Path

@staticmethod
def _path_to_uri(file_path: str) -> str:
    if file_path.startswith("file://"):
        return file_path
    return Path(file_path).as_uri()   # Correctly percent-encodes path components

And in LspClient.initialize():

from pathlib import Path

workspace_uri = workspace_path
if not workspace_uri.startswith("file://"):
    workspace_uri = Path(workspace_path).as_uri()

Category

boundary

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_<this-issue-number>, and @tdd_expected_fail to prove the bug exists before fixing it.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: bug-hunter

## Bug Report: [boundary] — Missing percent-encoding in `file://` URI construction ### Severity Assessment - **Impact**: Any file path or workspace path containing spaces, hash signs, percent signs, or other URI-reserved characters produces an invalid `file://` URI. LSP servers that strictly validate URIs will reject the request or correlate diagnostics to the wrong URI. This is a silent correctness bug — the runtime returns no error, but diagnostics/completions/hover results are silently lost or misattributed. - **Likelihood**: Medium — workspace paths with spaces (`/home/user/My Project`) are common on Windows and macOS developer machines, and are not unusual on Linux. - **Priority**: Medium ### Location - **File**: `src/cleveragents/lsp/runtime.py` - **Function**: `LspRuntime._path_to_uri()` — lines 366–370 - **File**: `src/cleveragents/lsp/client.py` - **Function**: `LspClient.initialize()` — lines 214–217 ### Description Both `_path_to_uri` and `initialize` construct `file://` URIs by simple string concatenation, without percent-encoding the path components: ```python # runtime.py line 370 return f"file://{file_path}" # client.py line 217 workspace_uri = f"file://{workspace_path}" ``` Per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and the LSP specification, `file://` URIs must be percent-encoded. For example: - `/home/user/my project/main.py` → `file:///home/user/my%20project/main.py` - `/tmp/work#1/file.py` → `file:///tmp/work%231/file.py` Without encoding, the URI `file:///home/user/my project/main.py` has an unencoded space, which is syntactically invalid. LSP servers (e.g., pyright, rust-analyzer) reject or misparse such URIs. The result is that: 1. `textDocument/publishDiagnostics` notifications from the server reference the correct percent-encoded URI. 2. `client.get_diagnostics(uri)` looks up `self._diagnostics[uri]` using the **unencoded** URI. 3. The lookup misses because the server stored diagnostics under the encoded URI. 4. `get_diagnostics` silently returns an empty list. ### Evidence ```python # runtime.py lines 365-370 @staticmethod def _path_to_uri(file_path: str) -> str: """Convert a filesystem path to a ``file://`` URI.""" if file_path.startswith("file://"): return file_path return f"file://{file_path}" # ← no percent-encoding! ``` ```python # client.py lines 214-217 workspace_uri = workspace_path if not workspace_uri.startswith("file://"): workspace_uri = f"file://{workspace_path}" # ← no percent-encoding! ``` The Python standard library provides `pathlib.Path.as_uri()` (Python 3.4+) which correctly encodes path components, or `urllib.parse.urlencode` for manual construction. ### Expected Behavior `_path_to_uri("/home/user/my project/main.py")` should return `"file:///home/user/my%20project/main.py"`. ### Actual Behavior `_path_to_uri("/home/user/my project/main.py")` returns `"file:///home/user/my project/main.py"` — an invalid URI with an unencoded space. ### Suggested Fix Use `pathlib.Path.as_uri()`: ```python from pathlib import Path @staticmethod def _path_to_uri(file_path: str) -> str: if file_path.startswith("file://"): return file_path return Path(file_path).as_uri() # Correctly percent-encodes path components ``` And in `LspClient.initialize()`: ```python from pathlib import Path workspace_uri = workspace_path if not workspace_uri.startswith("file://"): workspace_uri = Path(workspace_path).as_uri() ``` ### Category boundary ### TDD Note After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: `@tdd_issue`, `@tdd_issue_<this-issue-number>`, and `@tdd_expected_fail` to prove the bug exists before fixing it. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: bug-hunter
HAL9000 added this to the v3.2.0 milestone 2026-04-09 21:52:44 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#6583
No description provided.