Not found: Table was not found in location
BigQuery says a table was not found only when it was willing to look. The same typo can give you Access Denied instead, and which one you get tells you something useful about your access.
Not found: Table bigquery-public-data:samples.Shakespeare was not found in location US
What BigQuery means
The dataset was readable and the table was not in it.
BigQuery only answers “not found” for a table when you are allowed to list the dataset’s tables. If you cannot, it refuses to confirm or deny the table at all and you get Access Denied instead.
So the two messages are a crude access probe:
| Message | What it tells you |
|---|---|
Not found: Table PROJECT:DATASET.TABLE was not found in location L | you can read the dataset. The table name is wrong, or the table is not there yet. |
Not found: Dataset PROJECT:DATASET was not found in location L | you can read the project. The dataset is not there in that location. |
Access Denied: Table PROJECT:DATASET.TABLE: User does not have permission ... | BigQuery never looked. You cannot read the dataset, or it does not exist. |
The location at the end is where the job ran, not a property of the table. See Not found: Dataset was not found in location for the region side of this.
The smallest query that reproduces it
Correct project, correct dataset, wrong case on the table name:
SELECT 1 FROM `bigquery-public-data.samples.Shakespeare`
The table is shakespeare. Table names are case-sensitive, and so are dataset names, but a wrong-case dataset lands in the Access Denied bucket instead:
SELECT 1 FROM `bigquery-public-data.SAMPLES.shakespeare`
Access Denied: Table bigquery-public-data:SAMPLES.shakespeare: User does not have permission to query table bigquery-public-data:SAMPLES.shakespeare, or perhaps it does not exist.
Two typos of the same kind, two different messages, because of which lookup failed first.
The fix
Work through the name one part at a time.
- Case.
Ordersandordersare different tables. Copy the name from the schema browser rather than retyping it. - Backticks. All three of
`my-project.ds.t`,`my-project`.ds.tand the baremy-project.ds.tresolve in aFROMclause. If you quote the whole path in one pair of backticks, though, the table gets no short implicit alias, sot.colelsewhere in the query fails withUnrecognized name. - The dataset actually holds it. A table created in
DWH_DEVis not inDWH. Scheduled jobs that write to one and read from the other are the usual way this happens. - Freshly created tables. A
CREATE TABLEin the same script runs before theSELECT, but a table created by another job a second ago can still be invisible to a dry run. - Date-sharded names.
events_20260101exists,events_20260132does not. If you build the suffix with string formatting, print it.
Variants
| Message | Cause |
|---|---|
Table "orders" must be qualified with a dataset (e.g. dataset.table). | no dataset in the name and no default dataset on the job |
Not found: Dataset PROJECT:DATASET was not found in location L | the dataset part is wrong, or the dataset is in another region |
Unrecognized name: orders at [1:31] | a column resolution error, not a table one. The name was used where a column was expected. |
PROJECT:DATASET.events_* does not match any table. | a wildcard table whose prefix matches nothing |
The job’s error_result.reason for the two “Not found” messages is notFound. The qualification and wildcard messages come back as invalid, and Unrecognized name as invalidQuery.
Wildcard tables
A wildcard prefix that matches no table at all is its own error, and it says so plainly:
SELECT COUNT(*) FROM `my-project.logs.evnts_*`
my-project:logs.evnts_* does not match any table.
If the prefix is right but the query returns no rows, the problem is the _TABLE_SUFFIX filter instead. A SELECT table_name FROM my-project.logs.INFORMATION_SCHEMA.TABLES shows what the suffixes actually look like. See Wildcard tables.
In Querylab.io
Querylab.io dry-runs the query as you type and puts the message on the table name in the editor, for free, before you run anything.