{"record":{"id":"78e57083e5bd30ed","repo":"neoclide/coc.nvim","slug":"uri-changed-before-apply-edit","errorCode":null,"errorMessage":"${uri} changed before apply edit","messagePattern":"(.+?) changed before apply edit","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/core/files.ts","lineNumber":634,"sourceCode":"  public async redoWorkspaceEdit(): Promise<void> {\n    let { editState } = this\n    if (!editState || editState.applied) {\n      void this.window.showWarningMessage(`No workspace edit to redo`)\n      return\n    }\n    this.editState = undefined\n    await this.applyEdit(editState.edit)\n  }\n\n  public validateChanges(documentChanges: ReadonlyArray<DocumentChange>): void {\n    let { documents } = this\n    for (let change of documentChanges) {\n      if (TextDocumentEdit.is(change)) {\n        let { uri, version } = change.textDocument\n        let doc = documents.getDocument(uri)\n        if (typeof version === 'number' && version > 0) {\n          if (!doc) throw errors.notLoaded(uri)\n          if (doc.version != version) throw new Error(`${uri} changed before apply edit`)\n        } else if (!doc && !isFile(uri)) {\n          throw errors.badScheme(uri)\n        }\n      } else if (CreateFile.is(change) || DeleteFile.is(change)) {\n        if (!isFile(change.uri)) throw errors.badScheme(change.uri)\n      } else if (RenameFile.is(change)) {\n        if (!isFile(change.oldUri) || !isFile(change.newUri)) {\n          throw errors.badScheme(change.oldUri)\n        }\n      }\n    }\n  }\n\n  public async findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Promise<URI[]> {\n    let folders = this.workspaceFolderControl.workspaceFolders\n    if (token?.isCancellationRequested || !folders.length || maxResults === 0) return []\n    maxResults = maxResults ?? Infinity\n    let roots = folders.map(o => URI.parse(o.uri).fsPath)","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/core/files.ts#L616-L652","documentation":"LSP TextDocumentEdit changes are version-checked: each change specifies the document version the edit was computed against. When coc's local document version no longer matches (or a versioned edit targets a document not loaded), applying the edit is refused to prevent corrupting the buffer with stale edits.","triggerScenarios":"A language server returns WorkspaceEdit with textDocument.version set while the user edited/saved the buffer since the request started; edits applied after slow code actions or formatting on a rapidly changing buffer; document closed and reopened between request and response.","commonSituations":"Race between typing and applying code actions/format from LSP; long-running refactor while user keeps editing; async formatting on save racing with further changes.","solutions":["Re-trigger the code action/format after the buffer settles so the server computes edits against the current version","Avoid editing the buffer while a long refactor/format is in flight","Update the language server; stale-version races are often server bugs","Retry the operation; the buffer itself is left untouched (operation is atomic before any change)"],"exampleFix":"// before\nconst edit = await session.codeAction(...)\nawait workspace.applyEdit(edit) // may throw if buffer changed\n// after\nconst edit = await session.codeAction(...)\ntry { await workspace.applyEdit(edit) } catch { notify('Buffer changed, please retry') }","handlingStrategy":"retry","validationCode":"// compare versions before applying an LSP edit\nconst doc = workspace.getDocument(editUri)\nconst versioned = edit.documentChanges?.find(c => c.textDocument?.uri === editUri)\nif (doc && versioned && typeof versioned.textDocument.version === 'number'\n  && doc.version !== versioned.textDocument.version) {\n  return notify('Stale edit; re-run the action')\n}","typeGuard":"const isCurrentEdit = (doc, change) =>\n  !change || typeof change.textDocument?.version !== 'number' ||\n  change.textDocument.version <= 0 || doc?.version === change.textDocument.version","tryCatchPattern":"try {\n  await workspace.applyEdit(edit)\n} catch (e) {\n  if (String(e.message).includes('changed before apply edit')) notify('Buffer changed; retry the action')\n  else throw e\n}","preventionTips":["Don't edit the buffer while a code action/format request is in flight","Re-request actions instead of caching WorkspaceEdits","Keep language servers updated; stale-version races are often server bugs","Apply edits promptly after receiving them"],"tags":["lsp","race-condition","document-version","apply-edit"],"backgroundTag":"document-version-mismatch","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}