Skip to main content

ERPNext how-to & fixes

ModuleNotFoundError: workspace_settings — the two-line fix after a Frappe v15 or v16 upgrade

Frappe removed the Workspace Settings DocType, but an old Navbar Item row can still point at it and crash the desk. The two-line bench console fix.

Updated 12 Oct 20269 min readBy ManojFirst published 24 Jul 2026

You upgraded, bench migrate finished clean, then the desk refused to load with ModuleNotFoundError: No module named 'frappe.desk.doctype.workspace_settings'. Nothing you deployed references that module, because Frappe itself removed it. What still references it is a leftover row in your tabNavbar Item table from the version you upgraded from. This post pins the commits that removed the module, gives the two-line bench console fix, and adds the pre-upgrade checklist and nightly smoke test that would have caught it before your users did.

The forum thread that shows this most cleanly is I have issues with ERPNext after update on discuss.frappe.io: same signature, same fix. I am Manoj, ERPNext and Frappe implementation lead at MithTech, a team of Frappe specialists in Bengaluru. We run this playbook on every upgrade for our self-hosted clients.

Does your traceback match this bug?

If the traceback is still in your terminal, paste it in. The matcher is a client-side regex (nothing leaves the browser) and tells you whether your error is this regression or a lookalike.

InteractiveError Signature Matcher

Client-side regex match on ModuleNotFoundError + workspace_settings to tell you whether your traceback matches this post's regression.

Paste your traceback

Drop the exact traceback ERPNext showed you. Client-side match, no upload.

· ModuleNotFoundError
· workspace_settings
· navbar / controller

What actually changed in Frappe?

Workspace Settings used to be a real Frappe DocType. In our version-16 checkout (Frappe 16.27.1), the directory frappe/desk/doctype/workspace_settings/ is absent, and so is every reference to it in Python and JavaScript outside the translation .po files.

git log on that directory shows the revert (committer dates; the develop and version-16 commits are the same change):

a0568ae99e chore: revert workspace settings          (2026-01-22, version-16)
95d9568a80 chore: revert workspace settings          (2026-01-20, develop)
...
52ce3f9f9e feat: Workspace Settings - allow enabling and disabling of workspaces during setup
774d86f642 feat: Workspace Settings - allow enabling and disabling of workspaces during setup

The revert (first tag containing a0568ae99e: v16.3.0, released 27 January 2026) deletes eight files, including the entire DocType directory and one line from frappe/patches.txt:

Diff
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -239,7 +239,6 @@ frappe.patches.v15_0.migrate_session_data
 frappe.patches.v16_0.switch_default_sort_order
 frappe.integrations.doctype.oauth_client.patches.set_default_allowed_role_in_oauth_client
-execute:frappe.db.set_single_value("Workspace Settings", "workspace_setup_completed", 1)
 frappe.patches.v16_0.add_app_launcher_in_navbar_settings
 frappe.desk.doctype.workspace.patches.update_app

Two things to notice. First, the v15 line went through the same cycle much earlier: the DocType was backported in v15.38.0 (14 August 2024) and reverted in v15.39.0 (20 August 2024), and on 23 August 2024 v15 added a patch, frappe.patches.v15_0.remove_workspace_settings_navbar_items, that removes the navbar item on migrate. The v16 branch has no equivalent patch. Second, the line removed above is the one that used to seed the Workspace Settings singleton on migrate. Removing the seed is fine on new installs. It is not fine on sites that already have a Navbar Item row pointing at /app/workspace-settings from a build before the revert: v16.0 to v16.2, a pre-revert develop or beta build, or a v15.38.0 site moved to v16 without the later v15 patch.

That row is what breaks you.

Why does the desk crash when bench migrate succeeds?

bench migrate succeeds because none of the migration steps try to import the removed module. The moment a browser hits the desk, though, Frappe rebuilds the navbar from Navbar Settings and its child table of Navbar Item rows. Each Route-typed row that starts with /app/<something> implicitly asks Python to load a controller for <something>. For every current DocType this succeeds; for the stale Workspace Settings row it fails with:

ModuleNotFoundError: No module named 'frappe.desk.doctype.workspace_settings'

You can see the shape of that navbar rebuild in the patch that Frappe did keep — frappe/patches/v16_0/add_app_launcher_in_navbar_settings.py:11:

Python
navbar_settings = frappe.get_single("Navbar Settings")
settings_items = navbar_settings.as_dict().settings_dropdown

view_website_item_idx = -1
for i, item in enumerate(navbar_settings.settings_dropdown):
    if item.get("item_label") == "View Website":
        view_website_item_idx = i

settings_items.insert(
    view_website_item_idx + 1,
    {"item_label": "Apps", "item_type": "Route", "route": "/apps", "is_standard": 1},
)

That patch changes the same settings_dropdown child table your stale Workspace Settings row lives in. Migration knows how to add rows; on v16 it does not walk the table and evict rows whose target module has been deleted. Someone has to do that, and today that is you, in two lines.

What is the two-line fix?

Code recipeTwo-Line Fix Card

The literal bench console fix — delete the stale Navbar Item row, commit, clear cache — with copy buttons.

bench console · two lines · zero migrations
the two lines
frappe.db.delete("Navbar Item", {"item_label": "Workspace Settings"})
frappe.db.commit()
full session (with cache clear)
# On the ERPNext server, as the frappe user
bench --site your-site.local console

# Then, inside the Python console:
frappe.db.delete("Navbar Item", {"item_label": "Workspace Settings"})
frappe.db.commit()
exit()

# Finally, clear the cache so the navbar rebuilds without the stale row:
bench --site your-site.local clear-cache

Why this is safe

The row you are deleting is a Navbar Item pointing at a DocType Frappe itself removed. No user data, no business records, no accounting tables are touched. Take a routine DB backup first anyway — the checklist below covers that.

If your DB has more than one row (some sites carried both a top-level Navbar Item and a nested settings_dropdown row), the same delete matches both: the filter is on item_label, not parent. After bench clear-cache, refresh the desk. It boots.

What upgrade workflow catches this on staging?

This bug reaches production when the upgrade skips a rehearsal. This workflow surfaces it on staging instead.

1

Snapshot the production DB and sites/ directory

bench --site your-site backup --with-files; tar czf sites.tgz sites/. Copy both off the server. Keep a separate copy of site_config.json in your password manager: losing db_password or encryption_key is the one failure you cannot recover from.

2

Restore the backup onto a throwaway staging site

Same server or a spare VPS. bench new-site staging.local; bench --site staging.local restore /path/to/backup.sql.gz. Staging must run the same OS, Python, Node and MariaDB versions that production will run after the upgrade, or the rehearsal misses environment bugs.

3

Switch staging to the target Frappe / ERPNext branch and migrate

bench switch-to-branch version-16 frappe erpnext; bench --site staging.local migrate. Watch the logs. If migrate succeeds but the desk 500s, you have found this bug on staging — apply the two-line fix here, not in production.

4

Run the smoke test against staging

Hit /app/home and the top-5 workspace URLs. Log in as a real user role, not just Administrator. Open one Sales Invoice, one Purchase Receipt, one Stock Ledger view. Anything that 500s here would 500 in production tomorrow — fix it in staging.

5

Only then flip DNS / traffic to the upgraded instance

Keep the old instance running and reachable at a fallback hostname for at least the first hour after cutover. If a group of users hits a bug your smoke test missed, you can point DNS back without a restore.

InteractiveUpgrade Safety Checklist

Six pre-upgrade checkboxes — backups, dry-run migrate, staging smoke test, DNS cutover — that persist to your browser between sessions.

Pre-upgrade checklist · saved to your browser0/6 · 0%

What if devops owns the upgrade and accounts owns the workday?

The workflow above assumes one person can pause production, run a rehearsal and flip DNS at their own pace. Most SMEs do not work that way: devops queues the upgrade on a Friday evening, accounts finds the desk broken on Monday morning, and neither knows what changed over the weekend. This split works:

1

Devops owns the rehearsal and the rollback SLO

The pre-upgrade checklist and the smoke test belong to devops. If either fails, devops does not go to production. Accounts are not paged during the rehearsal.

2

Accounts owns the smoke-test URL list

Accounts writes down the five workspaces they open first every morning (usually Home, Accounting, Stock, Selling, Buying) and hands the list to devops, who put it in the smoke script. That is why the script below ships with those five defaults.

3

Both sign off on the rollback trigger

"If the desk 500s for more than N% of users within M minutes of cutover, we roll back." N and M live in a shared runbook, not in a chat message someone has to find during an outage.

If you upgrade often, automate step 3: an n8n workflow that polls the smoke-test endpoint every 5 minutes for the first 60 minutes after cutover and messages devops when the failure rate crosses the rollback threshold.

How does a nightly workspace smoke test prevent the next one?

The two-line fix clears today's incident. The nightly smoke test makes sure the next removed-module regression reaches your inbox at 02:30, not your users at 09:00.

Code recipePost-Upgrade Smoke Test

Three flavours of nightly workspace smoke test: Frappe Server Script (Scheduler), bench execute via cron, and a one-shot Python probe you can wire into any orchestration.

Nightly Server Script that fetches the top-5 workspace URLs and emails you if any return non-200 — so a workspace_settings-style regression is caught before users hit it.

workspace_smoke_test.py
# Server Script → Type: Scheduler Event → Frequency: Daily
# DocType: no linked DocType. Runs as Administrator.
import frappe
import requests
from frappe.utils import get_url

TOP_WORKSPACES = [
    "app/home",
    "app/accounting",
    "app/stock",
    "app/selling",
    "app/buying",
]

session = requests.Session()
base = get_url()
alerts = []

for path in TOP_WORKSPACES:
    try:
        r = session.get(f"{base}/{path}", timeout=10, allow_redirects=False)
        # 200 or a login-redirect (302 → /login) both mean the route resolves.
        if r.status_code not in (200, 302):
            alerts.append(f"{path} → HTTP {r.status_code}")
    except Exception as e:
        alerts.append(f"{path} → {type(e).__name__}: {e}")

if alerts:
    frappe.sendmail(
        recipients=["ops@yourco.in"],
        subject=f"[ERPNext smoke] {len(alerts)} workspace URL(s) failing",
        message="<pre>" + "\n".join(alerts) + "</pre>",
    )

What does each recipe check?

Route resolution

Every top-5 workspace URL returns 200 or a login redirect. A 500 from ModuleNotFoundError fails loudly. This is the check that would have caught workspace_settings on staging.

Controller import

get_controller("Workspace") runs for every Workspace row. If a Workspace's Python controller module has been removed, the assertion fails and the smoke test alerts.

Navbar target validity

Every Navbar Item with an /app/<something> route resolves to an existing DocType. This is the check that surfaces the exact class of bug this post fixes — a Navbar Item pointing at a DocType that no longer exists.

Independent of the desk

bench execute and the boot probe run without a browser. Even if the desk is 500-ing for humans, the smoke test still tells you whether the underlying controllers load.

What should an upgrade rollback SLO say?

What matters here is time to roll back. Your ops lead needs a written policy that says exactly when to abandon a failed upgrade and point traffic back to the old instance. Below is a starting draft for your ops or engineering lead to adjust and sign off:

Upgrade Rollback SLO (draft — for ops-lead review): Every ERPNext production upgrade must be preceded by (a) a full DB + sites/ backup less than 30 minutes old, and (b) a full migrate → smoke test dry-run on a staging site restored from that backup. The old production instance stays warm and reachable at a fallback hostname for at least 60 minutes post-cutover. If the new instance's smoke test fails any of the top-5 workspace URLs, or more than 1% of unique authenticated users hit a 500 within the first 30 minutes, devops rolls back within 15 minutes by re-pointing DNS to the fallback hostname. Rollback does not require a data restore because no schema-breaking migrations run on cutover day (schema changes run on the staging migrate the day before, are reviewed, and only then applied to production). Every rollback is followed within 48 hours by a written post-mortem naming the exact ModuleNotFoundError / ImportError / assertion that fired.

Why write it down rather than leave it to judgement on the day:

  • A written SLO is easy to defend. Auditors and enterprise buyers ask for one, and even a conservative policy beats "we roll back when it looks bad".
  • The numbers should match your traffic. 1% of authenticated users on a 20-user SME is zero users; on a 2,000-user distributor it is 20 people raising tickets. Scale N and M to your usage.
  • An hour of fallback is cheap. Keeping the old instance running for the first hour costs an hour of server time; the hosting cost comparison shows what a server costs.

Should you fix it now or backlog the process?

Fix immediatelyBacklog and fix the process
Desk currently 500-ing for real usersDesk boots; no user has hit the error
The 2-line delete + clear-cache — 60 secondsThe pre-upgrade rehearsal checklist and the nightly smoke test
Ops lead has 5 minutes and root shellOps lead has an afternoon and can restore staging
Post-mortem the same week, not the same hourPost-mortem is the deliverable, not an afterthought

Do both: the left column today, the right column this month.

At a glance

What to know
Symptombench migrate succeeds, then the desk fails with ModuleNotFoundError: No module named 'frappe.desk.doctype.workspace_settings'
CauseA stale Navbar Item row still routing to the removed Workspace Settings DocType
AffectedSites that ran a build with the DocType (v15.38.0, v16.0 to v16.2, or a pre-revert develop/beta build) and later upgraded
FixDelete the row in bench console, commit, bench clear-cache
PreventStaging rehearsal plus the nightly workspace smoke test

FAQ: what do people ask most?

+How do I fix the workspace_settings ModuleNotFoundError in ERPNext?

Take a DB backup, then in bench --site your-site.local console run frappe.db.delete("Navbar Item", {"item_label": "Workspace Settings"}) followed by frappe.db.commit(). Exit the console and run bench --site your-site.local clear-cache. Refresh the desk — it boots.

+Why does bench migrate succeed but the desk still crash?

Because migrate only runs the patches listed in frappe/patches.txt, and none of the current patches try to import the removed module. The desk boot process, however, rebuilds the navbar from Navbar Settings and tries to load a controller for every /app/<route> entry — the stale Workspace Settings row is what fails to import, not migrate itself.

+What version of Frappe / ERPNext does this apply to?

On the v16 line the DocType was reverted in commit a0568ae99e (22 January 2026), first released in v16.3.0. On the v15 line it existed only in v15.38.0 and was reverted in v15.39.0 (August 2024); v15 then added a patch that removes the navbar item on migrate, so a v15 site that migrates to a current v15 release is usually cleaned up. Sites that had the DocType and then moved to v16.3 or later can still carry the row.

+Is it safe to just delete the Navbar Item row like that?

Yes. Navbar Item is a UI configuration table — the row you delete is a link, not business data. No accounting records, no user data, no permissions are affected. The row's target DocType was already removed by Frappe itself; you are only removing the dangling reference.

+Can I run the fix through bench execute instead of dropping into console?

Yes: bench --site your-site.local execute frappe.db.delete --args "['Navbar Item', {'item_label': 'Workspace Settings'}]" — but you still need bench --site your-site.local execute frappe.db.commit after it, and the shell escaping is fiddly. The two-line console form is more legible for a runbook.

+What happens if we skip this and just leave the desk broken?

Every user hitting /app gets a 500. Background jobs (scheduler, workers) keep running, so scheduled emails and integrations still fire, but nobody can use the desk, and any client script or form event tied to a UI action is unreachable.

+Will this recur on the next upgrade?

Not for this row: once deleted it is gone. The same class of bug can recur when Frappe removes another DocType and leaves stale Navbar Item, Workspace Link or Report rows behind. The nightly smoke test in this post is built to catch that class, not just this instance.

+Do I need to run the same fix on every site in a multi-tenant bench?

Yes. Navbar Item is site-scoped, so the row lives in each site's database. Run the fix per site, or wrap it in a small shell script that loops bench --site $S execute … over your sites. The smoke test recipe above already works per site.

What is the bottom line?

Workspace Settings is not a bug in your instance; it is what is left of a DocType Frappe shipped briefly and removed. Deleting the stale row unblocks the desk in under a minute. The better question is why the upgrade reached production without a rehearsal. Fix that, and this class of bug stops reaching your users.

Your next step

Upgrading ERPNext and want the rehearsal done for you?

Next step · ERPNext how-to & fixes

Now see what an audit finds in yours

When the same problems keep coming back, tick what you see and get an audit of the ERPNext you have, then fixes in order of what costs you most.

Comments & ratings

No comments yet. Start a new discussion.

0

Manoj · Published on 24 July 2026

Worked with us? Leave a Google review

Questions about this? Talk to the team.

Book a 30-minute Google Meet, WhatsApp us, or send a message. The engineer who works on this replies.

Send us a message
Your details go to our team's CRM and are used only to reply.