{"record":{"id":"ec5dff208a1879b2","repo":"Budibase/budibase","slug":"unable-to-revert-err","errorCode":null,"errorMessage":"Unable to revert. ${err}","messagePattern":"Unable to revert\\. (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/dev/devRevertProcessor.ts","lineNumber":76,"sourceCode":"      source: productionAppId,\n      target: appId,\n    })\n\n    try {\n      await replication.rollback()\n\n      // update appID in reverted app to be dev version again\n      const db = context.getWorkspaceDB()\n      const appDoc = await db.get<Workspace>(DocumentType.WORKSPACE_METADATA)\n      appDoc.appId = appId\n      appDoc.instance._id = appId\n      await db.put(appDoc)\n      await cache.workspace.invalidateWorkspaceMetadata(appId)\n      await events.app.reverted(appDoc)\n\n      return { message: \"Reverted changes successfully.\" }\n    } catch (err) {\n      throw new Error(`Unable to revert. ${err}`, { cause: err })\n    } finally {\n      await replication.close()\n    }\n  }\n}\n\nexport function devRevertProcessor(): DevRevertProcessor {\n  if (!_devRevertProcessor) {\n    _devRevertProcessor = new DevRevertProcessor()\n  }\n  return _devRevertProcessor\n}\n\nexport async function revertDevChanges(data: DevRevertQueueData) {\n  const processor = devRevertProcessor()\n  const result = await processor.execute(data)\n  return result\n}","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/dev/devRevertProcessor.ts#L58-L94","documentation":"devRevertProcessor.revertApp performs a replication-based revert of a development app back to its production state, then emits a reverted event. Any failure inside the revert flow (db.put, metadata invalidation, replication errors) is caught and re-thrown wrapped in 'Unable to revert. <err>' with the original error as cause.","triggerScenarios":"Calling the revert app endpoint when replication from prod to dev fails: missing/locked dev appDoc, CouchDB replication conflicts, or appDoc put failures inside the try block.","commonSituations":"Developers clicking 'Revert changes' in the builder while the dev database has replication conflicts; the production app doc was deleted so the revert target no longer exists; CouchDB connection issues mid-replication.","solutions":["Inspect err.cause (the wrapped original error) - it names the real failure (e.g. replication conflict).","Check CouchDB replication conflict documents (_conflicts) in the dev app DB and resolve/delete them.","Verify the production appDoc still exists; recreate/republish the app if it was removed.","Retry the revert after resolving conflicts; if persistent, delete the dev app and create a fresh one from prod."],"exampleFix":"// diagnosing\ntry { await sdk.dev.revertApp(appId) } catch (e) { console.log(e.cause) }\n// resolution: resolve _conflicts in the dev DB, then retry the revert","handlingStrategy":"try-catch","validationCode":"const prodDoc = await prodDb.get(appId).catch(() => null)\nif (!prodDoc) throw new Error(\"Cannot revert: production app doc missing\")","typeGuard":"const hasCause = (e: unknown): e is Error & { cause: Error } => e instanceof Error && e.cause instanceof Error","tryCatchPattern":"try {\n  await sdk.dev.revertApp(appId)\n} catch (e) {\n  console.error(\"Revert failed:\", e.cause ?? e)\n  // resolve replication conflicts reported by e.cause before retrying\n}","preventionTips":["Always inspect err.cause - the wrapper hides the real CouchDB error","Resolve _conflicts in the dev database before reverting","Ensure the production app doc exists prior to revert","Close/idle other replication jobs during revert"],"tags":["replication","couchdb","dev-workflow"],"backgroundTag":"replication-conflict","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}