neoclide/coc.nvim · warning

${uri} changed before apply edit

Error message

${uri} changed before apply edit

What it means

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.

Source

Thrown at src/core/files.ts:634

  public async redoWorkspaceEdit(): Promise<void> {
    let { editState } = this
    if (!editState || editState.applied) {
      void this.window.showWarningMessage(`No workspace edit to redo`)
      return
    }
    this.editState = undefined
    await this.applyEdit(editState.edit)
  }

  public validateChanges(documentChanges: ReadonlyArray<DocumentChange>): void {
    let { documents } = this
    for (let change of documentChanges) {
      if (TextDocumentEdit.is(change)) {
        let { uri, version } = change.textDocument
        let doc = documents.getDocument(uri)
        if (typeof version === 'number' && version > 0) {
          if (!doc) throw errors.notLoaded(uri)
          if (doc.version != version) throw new Error(`${uri} changed before apply edit`)
        } else if (!doc && !isFile(uri)) {
          throw errors.badScheme(uri)
        }
      } else if (CreateFile.is(change) || DeleteFile.is(change)) {
        if (!isFile(change.uri)) throw errors.badScheme(change.uri)
      } else if (RenameFile.is(change)) {
        if (!isFile(change.oldUri) || !isFile(change.newUri)) {
          throw errors.badScheme(change.oldUri)
        }
      }
    }
  }

  public async findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Promise<URI[]> {
    let folders = this.workspaceFolderControl.workspaceFolders
    if (token?.isCancellationRequested || !folders.length || maxResults === 0) return []
    maxResults = maxResults ?? Infinity
    let roots = folders.map(o => URI.parse(o.uri).fsPath)

View on GitHub (pinned to 50e974d969)

Solutions

  1. Re-trigger the code action/format after the buffer settles so the server computes edits against the current version
  2. Avoid editing the buffer while a long refactor/format is in flight
  3. Update the language server; stale-version races are often server bugs
  4. Retry the operation; the buffer itself is left untouched (operation is atomic before any change)

Example fix

// before
const edit = await session.codeAction(...)
await workspace.applyEdit(edit) // may throw if buffer changed
// after
const edit = await session.codeAction(...)
try { await workspace.applyEdit(edit) } catch { notify('Buffer changed, please retry') }
Defensive patterns

Strategy: retry

Validate before calling

// compare versions before applying an LSP edit
const doc = workspace.getDocument(editUri)
const versioned = edit.documentChanges?.find(c => c.textDocument?.uri === editUri)
if (doc && versioned && typeof versioned.textDocument.version === 'number'
  && doc.version !== versioned.textDocument.version) {
  return notify('Stale edit; re-run the action')
}

Type guard

const isCurrentEdit = (doc, change) =>
  !change || typeof change.textDocument?.version !== 'number' ||
  change.textDocument.version <= 0 || doc?.version === change.textDocument.version

Try / catch

try {
  await workspace.applyEdit(edit)
} catch (e) {
  if (String(e.message).includes('changed before apply edit')) notify('Buffer changed; retry the action')
  else throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/78e57083e5bd30ed. Report an issue: GitHub.