cannot import name 'Table' from 'frappe.query_builder': Use DocType
You pull a colleague's custom app onto a freshly upgraded bench, run bench migrate, and the whole site refuses to boot. Somewhere deep in a controller, or worse, in a scheduled job that only runs at 2am, Python throws an ImportError and takes the workers down with it. The line it points at looks completely innocent: it is just importing something from frappe.query_builder.
The import used to work. It worked on the version the code was written against. Nobody changed that line. That is exactly why this one wastes an afternoon: the code is fine, your understanding of the module is out of date, and Frappe gave you a generic Python error instead of a helpful "this moved in v14" message.
The short version is that Table was never a stable part of the frappe.query_builder public surface, and the thing you actually want is DocType. Here is the correct import, why the old one broke, and how to write it so it survives the next upgrade too.
Fix: replace from frappe.query_builder import Table with from frappe.query_builder import DocType, and reference a table as DocType("Sales Invoice") instead of Table("tabSales Invoice"). Build the query with frappe.qb. If you must support several Frappe versions from one codebase, wrap the import in a try/except ImportError and fall back, or resolve the symbol at runtime.
The exact error: cannot import name 'Table' from 'frappe.query_builder'
The traceback ends like this, and the exact string is what you are probably searching for:
Traceback (most recent call last):
File "apps/your_app/your_app/api.py", line 8, in <module>
from frappe.query_builder import Table
ImportError: cannot import name 'Table' from 'frappe.query_builder'
(/home/frappe/frappe-bench/apps/frappe/frappe/query_builder/__init__.py)
You will also see it surface indirectly. Because the import runs at module load time, a single bad import in one file can cascade: bench migrate fails, the scheduler logs a repeating traceback, or the site returns a 500 with Internal Server Error and the real cause is only in bench --site yoursite console or the worker logs.
Why this happens: the query builder was rewritten around pypika
Frappe's query builder is a thin, opinionated layer on top of pypika, a Python library that builds SQL as expressions instead of strings. When Frappe adopted pypika (this landed in the v13/v14 era and became the recommended way to write queries), the module's job changed: instead of exposing raw pypika primitives, it started exposing Frappe-flavoured helpers that understand DocTypes and the tab table-name prefix for you.
The important part is what did not happen. There was no deprecation shim. The maintainers did not keep a Table alias around that prints a warning and points you at the new name. The symbol simply stopped being importable from that path, so any code (or tutorial, or Stack Overflow answer) written against an earlier internal layout fails with a flat ImportError that tells you nothing about versions.
If you look at what frappe.query_builder actually re-exports today, the Frappe-specific names come from frappe.query_builder.utils:
# frappe/query_builder/__init__.py, roughly
from pypika import * # pypika primitives, including its own Table
from frappe.query_builder.utils import (
Column,
DocType,
get_query,
get_query_builder,
patch_all,
)
So DocType is a first-class export. Table is not one of the Frappe helpers; it only exists at all because of the pypika wildcard, and relying on a name that leaks through a wildcard import is exactly the kind of thing that breaks between versions. The right move is not to hunt down pypika's Table. It is to use DocType, which is the name Frappe actually maintains.
The fix, step by step
Step 1: Confirm what your version actually exports
Do not guess. Open the console on the exact site and ask the module directly:
bench --site yoursite.local console
import frappe.query_builder as qb
# What can I import by name from this module?
print([n for n in dir(qb) if not n.startswith("_")])
# Is DocType here?
print("DocType" in dir(qb)) # True on any supported version
# Where does DocType come from?
from frappe.query_builder import DocType
print(DocType) # <class 'frappe.query_builder.utils.DocType'>
This is the single most useful habit for this whole class of bug. Instead of trusting a tutorial, you interrogate the running code. If DocType shows up in that list (it will), you know your import target before you touch a file.
Step 2: Replace the import and the call site
Change the import and the way you reference the table. The old pypika-style code and the Frappe-native version look like this side by side:
-from frappe.query_builder import Table
-
-invoice = Table("tabSales Invoice")
-query = (
- frappe.qb.from_(invoice)
- .select(invoice.name, invoice.grand_total)
- .where(invoice.docstatus == 1)
-)
+from frappe.query_builder import DocType
+
+invoice = DocType("Sales Invoice")
+query = (
+ frappe.qb.from_(invoice)
+ .select(invoice.name, invoice.grand_total)
+ .where(invoice.docstatus == 1)
+)
results = query.run(as_dict=True)
Two things to notice. First, you pass the human DocType name, "Sales Invoice", not the physical table name "tabSales Invoice". DocType adds the tab prefix for you, which is the whole point of using it. Second, frappe.qb is the query-builder entry point on the frappe namespace, so you rarely need to import anything from query_builder at all beyond DocType.
Step 3: Rebuild assets and restart if this was in a loaded module
Because the failing import ran at module import time, the workers cached the broken state. After editing, restart so every process reloads:
bench --site yoursite.local clear-cache
bench restart # or: sudo supervisorctl restart all
On a development bench, bench start already reloads on file changes, but the scheduler and background workers do not always pick up an import-time failure cleanly, so a restart removes any doubt.
Step 4: Make the import version-tolerant if you ship to many sites
If you maintain an app that has to install on client sites running different Frappe versions, do not pin your code to one internal layout. Guard the import and fall back to resolving the symbol at runtime:
try:
from frappe.query_builder import DocType
except ImportError: # extremely old layout or a partial install
# frappe.qb.DocType is available wherever the query builder is
DocType = frappe.qb.DocType
The reason this works is that frappe.qb exposes the same helpers as attributes, so even if the top-level module path shifts again, the query-builder object itself still hands you DocType. Prefer this over a hard version check like if frappe.__version__ >= ..., which is brittle and gets stale. Test behaviour, not version numbers. The same principle applies when you override a method in Frappe or ERPNext: bind to the stable public name, not to an internal path that happens to exist today.
Verification
Prove the fix from the console before you call it done. A clean query and a clean migrate are your two signals:
bench --site yoursite.local console
>>> from frappe.query_builder import DocType
>>> inv = DocType("Sales Invoice")
>>> frappe.qb.from_(inv).select(inv.name).limit(1).run(as_dict=True)
[{'name': 'ACC-SINV-2026-00001'}]
bench --site yoursite.local migrate
# ends with: *** Scheduler is disabled *** (or enabled) and no traceback
If the query returns rows and migrate completes without the ImportError, the module now loads on this version. Tail the worker log for one scheduler cycle to be sure the 2am job path is clean too:
bench --site yoursite.local doctor
tail -f logs/worker.log
What people get wrong
Installing pypika manually to "get Table back". This is the most common wrong turn. Someone sees a pypika name is missing and runs pip install pypika or pins a specific pypika version inside the bench. Do not. Frappe vendors a compatible pypika and controls its version; forcing a different one can break the query builder for the entire bench, not just your app. The missing name is not a missing dependency.
Importing Table straight from pypika. You can write from pypika import Table and it will import. But then you are back to passing "tabSales Invoice" by hand, you lose the DocType-awareness, and you have coupled your app to pypika's public API instead of Frappe's. When Frappe next bumps pypika, you own that risk alone. Use DocType.
Downgrading Frappe to match the tutorial. Rolling the whole bench back to an old version so one import works is trading a five-minute code change for a permanent maintenance liability. If you are deliberately choosing a branch, do it on purpose and know why, as covered in switching from the developer to the official release branch. Do not downgrade to avoid renaming an import.
When it is still broken
If DocType imports fine but you still get an error, work through these:
- A different name is the real culprit. Read the traceback's last line again. If it says
cannot import name 'X'for something other thanTable, run the Step 1 console check for that name. The pattern is identical: find the maintained export, use it. - The error moved into pypika itself. If the traceback now points inside
pypika/, your app may be calling a pypika method that changed. Reproduce the exact query in the console and build it up expression by expression until the failing call reveals itself. - Stale compiled files. A leftover
.pycor a half-copied app can import an old module. Runfind apps/your_app -name '__pycache__' -type d -exec rm -rf {} +andbench build --app your_app, then restart. - You are on a partial or broken install. If
frappe.qbitself is undefined in the console, the framework is not fully installed on that site. Re-runbench --site yoursite migrateand check thatfrapperesolves to the app inside your bench, not a stray system-wide pip install.
The habit to keep from all of this: when an import breaks after an upgrade, do not search for the old name. Open a console on the running site, list what the module exports now, and bind to the name Frappe actually maintains. It turns a version-mismatch mystery into a thirty-second lookup.
Frequently asked questions
- What is the correct import to replace 'from frappe.query_builder import Table'?
- Use 'from frappe.query_builder import DocType' and reference a table as DocType("Sales Invoice"). DocType is a maintained Frappe export and adds the 'tab' table prefix for you, so you pass the DocType name rather than the physical table name. Table only ever leaked through a pypika wildcard import and is not a stable part of the module's public surface.
- Which Frappe version broke the Table import?
- The query builder was rewritten around the pypika library in the v13/v14 era, and that rework changed what frappe.query_builder re-exports. There was no deprecation alias for Table, so code written against an older internal layout fails with a flat ImportError rather than a version-mismatch warning. Rather than pin to a version, check what your installed version exports from a bench console.
- How do I see what frappe.query_builder actually exports on my version?
- Open 'bench --site yoursite console' and run: import frappe.query_builder as qb; print([n for n in dir(qb) if not n.startswith('_')]). That lists the real, current exports for the exact version you are running. Then bind your import to a name that appears in that list, such as DocType, instead of trusting a tutorial written for a different release.
- Should I pip install pypika to fix the missing Table name?
- No. Frappe vendors a specific compatible pypika version and manages it for you; installing or pinning your own can break the query builder for the whole bench. The missing name is not a missing dependency. The fix is to use the maintained DocType export, not to alter pypika.