{"record":{"id":"448eef05be31aa27","repo":"astral-sh/ruff","slug":"workspace-not-found-uri","errorCode":null,"errorMessage":"Workspace not found: {uri}","messagePattern":"Workspace not found: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/ty_server/src/session.rs","lineNumber":804,"sourceCode":"    ///\n    /// # Errors\n    ///\n    /// This returns an error if the workspace folder has already been removed\n    /// or otherwise could not be found.\n    pub(crate) fn remove_workspace_folder(\n        &mut self,\n        client: &Client,\n        uri: &Uri,\n    ) -> anyhow::Result<()> {\n        tracing::info!(\"Removing workspace folder: {uri}\");\n\n        let path = uri\n            .to_file_path()\n            .map_err(|()| anyhow!(\"Workspace URI is not a file path: {uri}\"))?;\n        let workspace_path = SystemPathBuf::from_path_buf(path)\n            .map_err(|path| anyhow!(\"Workspace path is not valid UTF-8: {}\", path.display()))?;\n\n        anyhow::ensure!(\n            self.workspaces.unregister(&workspace_path),\n            \"Workspace not found: {uri}\",\n        );\n\n        // Note that it is somewhat unclear whether we actually need to\n        // clear diagnostics here. It seems that, at least in the case\n        // of VS Code, it will auto-clear any diagnostics not found in\n        // the workspace diagnostic response. Moreover, VS Code will\n        // re-request workspace diagnostics after removing a workspace\n        // folder.\n        //\n        // For now, we keep unconditionally clearing diagnostics on\n        // opened text documents for reasons of good sense, but it's\n        // possible that we don't even need to do that (when workspace\n        // diagnostics are enabled).\n        //\n        // See: https://github.com/astral-sh/ruff/pull/22953#discussion_r2745255350\n","sourceCodeStart":786,"sourceCodeEnd":822,"githubUrl":"https://github.com/astral-sh/ruff/blob/672bb4edf04c84f8b0753346359daee4057158f2/crates/ty_server/src/session.rs#L786-L822","documentation":"A workspace/didChangeWorkspaceFolders removal targeted a path that is not registered in the server's workspace map, so workspaces.unregister() returned false and the ensure! fired (session.rs:804). The server only knows folders it successfully registered during initialize or a prior add.","triggerScenarios":"Removing a folder that was never added, removing the same folder twice, removing a folder whose earlier add failed (e.g. it hit the non-file-URI error), or a path/URI normalization mismatch (trailing slash, case, symlink).","commonSituations":"Editor restarts replaying folder state, duplicate remove events, races between add and remove during rapid project switching, or URI-vs-canonical-path discrepancies.","solutions":["Track added workspace folders client-side and only remove ones you added","Ignore/log-and-continue on duplicate removal — state is already correct","Verify the URI string matches exactly (encoding, trailing slash) the one used when adding","Check the server log for a failed prior add of the same folder"],"exampleFix":"// before\nremoveWorkspaceFolder(uri);  // fired twice -> 'Workspace not found'\n\n// after\nconst added = new Set();\nfunction removeWorkspaceFolder(uri) {\n  if (!added.has(uri)) return;  // idempotent\n  added.delete(uri);\n  client.notify('workspace/didChangeWorkspaceFolders', {\n    event: { removed: [{ uri, name: uri }] },\n  });\n}","handlingStrategy":"validation","validationCode":"// TS: idempotent removal backed by a client-side registry\nconst addedFolders = new Set<string>();\nfunction removeFolder(uri: string) {\n  const key = vscode.Uri.parse(uri).toString();\n  if (!addedFolders.has(key)) return;\n  addedFolders.delete(key);\n  client.notify('workspace/didChangeWorkspaceFolders', {\n    event: { removed: [{ uri: key, name: key }] },\n  });\n}","typeGuard":"const isRegisteredFolder = (uri: string): boolean => addedFolders.has(normalizeUri(uri));","tryCatchPattern":null,"preventionTips":["Normalize URIs (resolve, strip trailing slashes) before comparing add/remove sets","Treat duplicate removals as no-ops instead of errors","If an add failed, don't queue the matching remove"],"tags":["lsp","workspace","did-change-workspace-folders","state-mismatch"],"backgroundTag":null,"analyzedSha":"672bb4edf04c84f8b0753346359daee4057158f2","analyzedAt":"2026-08-16T08:54:05.464Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}