name: frappe-ops-upgrades description: > Use when upgrading Frappe/ERPNext between major versions (v14 to v15, v15 to v16), troubleshooting failed migrations, or planning rollback. Prevents broken upgrades from skipped patches, incompatible customizations, and missing pre-upgrade checks. Covers version upgrade paths, bench update, migrate command, patch troubleshooting, rollback procedures, breaking changes per version. Keywords: upgrade, migration, v14, v15, v16, bench update, bench migrate, rollback, patches, breaking changes, update failed, bench update error, migration error, patches failing, rollback after upgrade.. license: MIT compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16." metadata: author: OpenAEC-Foundation version: "2.0"
Version Upgrades
Complete guide for upgrading Frappe/ERPNext between major versions, handling failed migrations, and rolling back safely.
Versions: v14 → v15 → v16
Quick Reference: Upgrade Commands
| Task | Command |
|---|---|
| Full update | bench update |
| Update specific app | bench update --pull --app erpnext |
| Switch branch | bench switch-to-branch version-15 frappe erpnext |
| Run migrations only | bench --site mysite migrate |
| Check migration readiness | bench --site mysite ready-for-migration |
| Backup before upgrade | bench --site mysite backup |
| Restore from backup | bench --site mysite restore /path/to/backup.sql.gz |
| Re-run failed patch | Add #YYYY-MM-DD suffix in patches.txt |
Decision Tree: Upgrade Strategy
Need to upgrade?
├── Single minor version bump (e.g., v15.10 → v15.20)?
│ └── YES → Run `bench update` directly
├── Major version jump (e.g., v14 → v15)?
│ ├── Have custom apps?
│ │ ├── YES → Test on staging FIRST, check breaking changes
│ │ └── NO → Follow standard upgrade path
│ └── Multiple major versions (v14 → v16)?
│ └── ALWAYS upgrade one version at a time: v14 → v15 → v16
└── Production environment?
├── YES → ALWAYS test on staging clone first
└── NO → Proceed with standard upgrade
Pre-Upgrade Checklist
ALWAYS complete these steps before ANY major version upgrade:
- Full backup —
bench --site mysite backup --with-files - Test on staging — Clone production to a staging bench and test there first
- Check breaking changes — Review the breaking changes section below
- Audit custom apps — Run custom apps against new version's API changes
- Check Python/Node versions — v15 requires Node 18+; v16 requires Node 24+, Python 3.14+
- Disable scheduler —
bench --site mysite scheduler disable - Check pending jobs —
bench --site mysite ready-for-migration - Read release notes — Check GitHub release notes for each version
Standard Upgrade Process
Step-by-Step
# 1. Backup all sites
bench backup-all-sites
# 2. Switch to target version branch
bench switch-to-branch version-15 frappe erpnext
# 3. Update (pulls code, installs deps, builds, migrates)
bench update
# 4. Verify
bench --site mysite migrate # if not done by update
bench version # confirm versions
What bench update Executes (In Order)
- Backup all sites
- Pull latest code for all apps (
git pull) - Install Python requirements (
pip install) - Install Node requirements (
yarn install) - Build static assets (
bench build) - Run migrations on all sites (
bench migrate) - Restart bench processes
v14 → v15 Breaking Changes
Environment Requirements
| Requirement | v14 | v15 |
|---|---|---|
| Node.js | v14+ | v18+ |
| Python packaging | setup.py | pyproject.toml |
Backend Breaking Changes
db.set()removed — Usedoc.db_set()insteaddb.sql()parameters removed —as_utf8andformattedno longer accepteddb.set_value()for Singles — Usefrappe.db.set_single_value()insteadjob_namedeprecated — Usejob_idparameter inenqueue()frappe.new_doc()arguments —parent_doc,parentfield,as_dictMUST be keyword argsfrappe.get_installed_apps()— No longer acceptssortorfrappe_lastargs- Method override order reversed — Last override now takes precedence
- Timezone functions renamed —
convert_utc_to_user_timezone→convert_utc_to_system_timezone
Frontend Breaking Changes
- Vue 2 → Vue 3 — All Vue components MUST be migrated
- Window globals removed —
get_today→frappe.datetime.get_today,user→frappe.session.user thisin Client Scripts — Local scope access no longer supported- Image lazy loading — Replace
website-image-lazyclass with nativeloading="lazy"
Security Changes
- Server Scripts disabled by default — Enable:
bench set-config -g server_script_enabled 1 - "Desk User" role added — Replaces "All" role for desk user permissions
currentsite.txtremoved — Usebench use sitenameorFRAPPE_SITEenv var
Removed Features
- Event Streaming moved to separate app
- Cordova support removed
setup.pyremoved (usepyproject.toml)--make_copyand--restorebuild flags removed (use--hard-link)
See breaking-changes.md for the complete list.
v15 → v16 Breaking Changes
Environment Requirements
| Requirement | v15 | v16 |
|---|---|---|
| Node.js | v18+ | v24+ |
| Python | 3.10+ | 3.14+ |
Backend Breaking Changes
- Default sort order changed —
creationinstead ofmodifiedfor all list queries has_permissionhooks — MUST return explicitTrue;Noneno longer acceptedfrappe.get_doc(doctype, name, field=value)— No longer updates values- DB commits in document hooks — No longer allowed to prevent data integrity issues
frappe.sendmail(now=True)— No longer commits transactions implicitlydb.get_value()for Singles — Now returns proper types instead of strings- State-changing methods require POST —
/api/method/logout,/api/method/upload_file, etc.
Separated Modules (Install Separately)
- Energy Points →
frappe/eps - Newsletter →
frappe/newsletter - Backup Integrations →
frappe/offsite_backups - Blog →
frappe/blog
Frontend Breaking Changes
- Report/Dashboard/Page JS evaluated as IIFEs (no global scope pollution)
- Awesome Bar redesigned, moved to sidebar (
Cmd+K) - List view right sidebar removed
/appsendpoint deprecated;/appreroutes to/desk
Configuration Changes
- Site config cached for up to one minute (changes not immediate)
- Country field requires valid ISO 3166 ALPHA-2 code
bench versionoutput format changed to "plain" (use-f legacyfor old format)override_doctypehook classes MUST inherit from the overridden class
See breaking-changes.md for the complete list.
Patch System
How Patches Work
Patches are one-off data migration scripts that run during bench migrate. They are defined in each app's patches.txt file.
patches.txt Format [v14+]
[pre_model_sync]
# Runs BEFORE schema sync — use for data prep
myapp.patches.v15_0.prepare_data_for_migration
[post_model_sync]
# Runs AFTER schema sync — use for data that needs new schema
myapp.patches.v15_0.migrate_data_to_new_fields
Patch Execution Rules
- Patches run in the order defined in
patches.txt - Each patch runs exactly ONCE — tracked in the
__patchestable - To re-run a patch, append a date comment:
myapp.patches.v15_0.fix #2025-03-20 - One-off statements:
execute:frappe.delete_doc('Page', 'old_page', ignore_missing=True)
Writing a Patch
# myapp/patches/v15_0/migrate_field_data.py
import frappe
def execute():
# ALWAYS reload if you need the NEW schema
frappe.reload_doc("module_name", "doctype", "doctype_name")
# Perform data migration
frappe.db.sql("""
UPDATE `tabSales Invoice`
SET new_field = old_field
WHERE old_field IS NOT NULL
""")
Debugging Stuck Patches
# Check which patches have run
bench --site mysite console
>>> frappe.db.sql("SELECT * FROM __patches WHERE patch LIKE '%stuck_patch%'")
# Remove a patch record to force re-run
>>> frappe.db.sql("DELETE FROM __patches WHERE patch = 'myapp.patches.v15_0.broken_patch'")
>>> frappe.db.commit()
# Then re-run migrate
bench --site mysite migrate
Rollback Procedure
Immediate Rollback (Within Hours)
# 1. Stop all processes
bench stop
# 2. Restore database from pre-upgrade backup
bench --site mysite restore /path/to/pre-upgrade-backup.sql.gz \
--with-public-files /path/to/files.tar \
--with-private-files /path/to/private-files.tar
# 3. Switch back to previous version branch
bench switch-to-branch version-14 frappe erpnext
# 4. Install old dependencies
bench setup requirements
# 5. Build old assets
bench build
# 6. Start bench
bench start # or: sudo bench restart (production)
Critical Rules for Rollback
- ALWAYS keep pre-upgrade backups for at least 7 days
- NEVER run
bench migrateafter restoring to old branch — schema is already correct - ALWAYS restore files alongside database — file references may break otherwise
- NEVER attempt rollback after users have created new data on the upgraded version
Frappe Packages: Moving Customizations Between Sites
Frappe Packages (v14+) are lightweight UI-built applications — bundles of Custom Module Defs distributed as .tar.gz tarballs. For Custom Fields, Property Setters, and DocPerms on standard DocTypes, use Fixtures instead.
Quick Reference: Package vs Fixtures vs App
| Mechanism | Use When | CLI Command |
|---|---|---|
| Package | UI-built DocTypes, Scripts, Web Pages | UI only (Package Import/Release) |
| Fixtures | Custom Fields, Property Setters, DocPerms | bench --site mysite export-fixtures |
| Frappe App | Full development workflow, CI/CD, tests | bench get-app, bench install-app |
Package Workflow (UI-Based)
- Create a Package document → assign Custom Module Defs to it
- Create a Package Release → exports to
[bench]/sites/[site]/packages/as[package]-[version].tar.gz - On target site, create Package Import → attach tarball, check Activate
- System migrates data like an app migration; use Force to overwrite existing files
Fixtures Workflow (CLI-Based)
# hooks.py — define what to export
fixtures = [
"Custom Field",
"Property Setter",
{"dt": "Client Script", "filters": [["module", "=", "My Module"]]}
]
# Export fixtures to JSON in your app
bench --site mysite export-fixtures --app myapp
# Fixtures auto-sync on: bench --site mysite migrate
NEVER use Packages to modify standard/core DocTypes — use a Frappe App with Fixtures.
See frappe-packages.md for the complete reference including decision trees, limitations, and best practices.
Custom App Compatibility Checks
Before upgrading, audit each custom app:
- Check deprecated APIs — Search for removed functions listed in breaking changes
- Check
setup.py— Must migrate topyproject.tomlfor v15+ - Check Vue components — Must be Vue 3 compatible for v15+
- Check
patches.txt— Ensure patches use[pre_model_sync]/[post_model_sync]sections [v14+] - Check hooks.py — Verify no removed hooks are used
- Run tests —
bench --site test_site run-tests --app myapp
Decision Tree: In-Place vs Fresh Install
Choosing upgrade strategy:
├── Small site (< 10 GB database)?
│ └── In-place upgrade is usually fine
├── Large site (> 50 GB database)?
│ ├── Many custom apps? → Fresh install + data migration
│ └── Standard apps only? → In-place with extended downtime window
├── Skipping multiple versions (v13 → v15)?
│ └── ALWAYS fresh install — sequential upgrades are too risky
└── Critical production with zero-downtime requirement?
└── Fresh install on parallel server + DNS switch
Version Differences Summary
| Feature | v14 | v15 | v16 |
|---|---|---|---|
| Python packaging | setup.py | pyproject.toml | pyproject.toml |
| Vue version | Vue 2 | Vue 3 | Vue 3 |
| Node.js minimum | v14 | v18 | v24 |
| Python minimum | 3.8 | 3.10 | 3.14 |
| Server Scripts | Enabled | Disabled default | Disabled default |
| Default sort | modified | modified | creation |
| patches.txt sections | Yes | Yes | Yes |
| Workspace sidebar | No | No | Yes |
| Separated modules | — | Event Streaming | Blog, Newsletter, EPS |
Reference Files
| File | Contents |
|---|---|
| examples.md | Complete upgrade workflow examples |
| anti-patterns.md | Common upgrade mistakes and fixes |
| breaking-changes.md | Detailed breaking changes per version |
| frappe-packages.md | Packages, fixtures, and moving customizations between sites |