{"record":{"id":"edcad11743d4ffd0","repo":"hcengineering/platform","slug":"no-valid-diff-possible-applying-op-path-json","errorCode":null,"errorMessage":"No valid diff possible applying ${op.path} ${JSON.stringify(error, undefined, 2)}","messagePattern":"No valid diff possible applying (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/text-editor-resources/src/components/diff/recreate.ts","lineNumber":196,"sourceCode":"      const afterStepJSON = clone(this.currentDoc) // working document receiving patches\n      const pathParts = op.path.split('/')\n\n      // collect operations until we receive a valid document:\n      // apply ops-patches until a valid prosemirror document is retrieved,\n      // then try to create a transformation step or retry with next operation\n      while (toDoc == null) {\n        applyPatch(afterStepJSON, [op])\n\n        try {\n          toDoc = this.schema.nodeFromJSON(afterStepJSON)\n          toDoc.check()\n        } catch (error: any) {\n          toDoc = null\n          if (this.ops.length > 0) {\n            op = this.ops.shift() as Operation\n            ops.push(op)\n          } else {\n            throw new Error(`No valid diff possible applying ${op.path} ${JSON.stringify(error, undefined, 2)}`)\n          }\n        }\n      }\n\n      // apply operation (ignoring afterStepJSON)\n      if (ops.length === 1 && (pathParts.includes('attrs') || pathParts.includes('type'))) {\n        // Node markup is changing\n        this.addSetNodeMarkup() // a lost update is ignored\n        ops = []\n      } else if (ops.length === 1 && op.op === 'replace' && pathParts[pathParts.length - 1] === 'text') {\n        // Text is being replaced, we apply text diffing to find the smallest possible diffs.\n        this.addReplaceTextSteps(op, afterStepJSON)\n        ops = []\n      } else if (this.addReplaceStep(toDoc, afterStepJSON)) {\n        // operations have been applied\n        ops = []\n      }\n    }","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/text-editor-resources/src/components/diff/recreate.ts#L178-L214","documentation":"recreateChangeContentSteps replays an editor diff by applying ProseMirror operations one at a time; when an operation throws, it shifts to the next queued op and retries, but if the op queue is exhausted it throws this error including the failing op path and the underlying error JSON. It means the recorded change set cannot be validly applied to the document (documents diverged beyond what the diff can bridge).","triggerScenarios":"Applying a stored diff/recreated change to a doc whose structure no longer matches (schema change, doc edited since the diff was captured); all ops fail and this.ops is empty so no further retry is possible.","commonSituations":"Collaborative documents modified concurrently while a diff was being replayed; editor schema upgraded so op paths (attrs/type) no longer exist; corrupted or hand-edited change records.","solutions":["Log op.path and the wrapped error to identify the first path that no longer matches the document schema","Recreate the diff between the actual current doc and target doc instead of replaying the stale one","Update the editor schema/migrations so legacy node types and attrs referenced by the ops still resolve","Guard the call site (init) with try-catch and fall back to re-rendering the document rather than crashing the editor"],"exampleFix":"// before\ntry {\n  await recreateChangeContentSteps(doc, change)\n} catch (e) { throw e }\n// after\ntry {\n  await recreateChangeContentSteps(doc, change)\n} catch (e) {\n  console.error('Diff replay failed, rebuilding diff from live doc', e)\n  await rebuildDiffFromDocs(currentDoc, targetDoc)\n}","handlingStrategy":"try-catch","validationCode":"function opPathsExistInSchema (ops: Operation[], schema: Schema): boolean {\n  return ops.every((op) => {\n    const parts = op.path.split('.')\n    return parts.every((p) => p in schema.nodes || p in schema.marks || ['attrs', 'type', 'content'].includes(p))\n  })\n}\n// if (!opPathsExistInSchema(change.ops, editor.schema)) rebuildDiff()","typeGuard":"function isApplicableOp (op: Operation, doc: ProsemirrorNode): boolean {\n  const from = (op as any).from\n  return from === undefined || (typeof from === 'number' && from <= doc.content.size)\n}","tryCatchPattern":"try {\n  recreateChangeContentSteps.call(recreator)\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('No valid diff possible')) {\n    console.error('Diff replay diverged:', err.message)\n    fallbackToFullRedraw(recreator.targetDoc)\n    return\n  }\n  throw err\n}","preventionTips":["Recompute diffs from the live document instead of replaying stale change records","Version your editor schema and migrate stored change ops on upgrade","Avoid concurrent edits during diff replay; lock or rebase first"],"tags":["prosemirror","diff","document-state"],"backgroundTag":"diff-application-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}