Skip to content
Microsoft SQL Server

SQL Server Error 4902

Cannot find the object because it does not exist or you do not have permissions

Applies to: All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
Last technically reviewed 24 October 2026 on SQL Server 2025 (17.0.1145.4)
The full message
Msg 4902, Level 16, State 1, Line 1
Cannot find the object "dbo.Invoices" because it does not exist or you do not have permissions.
Other forms of this message
TRUNCATE TABLE on a missing table raises 4701 with the same wording (reproduced)
Msg 4701, Level 16, State 1, Line 1
Cannot find the object "Invoices" because it does not exist or you do not have permissions.
ALTER TABLE by a user without ALTER permission on an existing table (reproduced, error 1088)
Msg 1088, Level 16, State 13, Line 1
Cannot find the object "Invoice" because it does not exist or you do not have permissions.
Error number
4902
Severity
16 (user-correctable error)
State
1
Product
Microsoft SQL Server Database Engine
Versions
All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
Last reviewed
24 October 2026

What it means

Error 4902 means an ALTER TABLE statement names a table that SQL Server cannot find under that name in the current database: a typo, the wrong schema, or a temporary table that no longer exists in this scope.

ALTER TABLE resolves its target before doing anything else: adding or dropping a column, enabling or disabling constraints (CHECK CONSTRAINT, NOCHECK CONSTRAINT), switching triggers. If the name does not resolve, it raises 4902 at severity 16. The quoted name is exactly what you wrote, schema included, so compare it character by character with the real table. The wording mentions permissions because SQL Server does not reveal whether a hidden object exists.

In practice the object is usually really missing. On SQL Server 2025 a user who lacked ALTER permission on an existing table received error 1088, with the same text, rather than 4902. Other statements use their own numbers for the same situation: TRUNCATE TABLE raises 4701, DROP raises 3701, and SELECT or INSERT raise 208. Temporary tables are a common source: a #table created inside dynamic SQL or a called procedure is dropped when that scope ends.

Common causes

  • A typo or a singular/plural mix-up in the table name (Invoices versus Invoice).
  • The table lives in another schema than the one in the script, often a script written for dbo run against a table in its own schema.
  • The script runs in the wrong database, or before the step that creates the table.
  • A temporary table created inside EXEC(...), sp_executesql or a stored procedure, then altered after that scope ended.
  • Constraint maintenance scripts (NOCHECK / WITH CHECK CHECK CONSTRAINT ALL) generated for a list of tables that has since changed.

Diagnose it

Read-only These queries only read metadata and dynamic management views. They do not change data, permissions or configuration, so they are safe to run first.

Find tables with a similar name in any schema

SELECT DB_NAME() AS current_db, SCHEMA_NAME(t.schema_id) AS schema_name, t.name AS table_name
FROM sys.tables AS t
WHERE t.name LIKE N'Invoice%'
ORDER BY schema_name, table_name;
What to look for

The real schema and spelling of the table. No rows: check current_db, you may be in the wrong database.

Check the exact name resolves before altering it

SELECT OBJECT_ID(N'dbo.Invoice', N'U') AS in_dbo,
       OBJECT_ID(N'billing.Invoice', N'U') AS in_billing;
What to look for

NULL for the name used in the failing statement; a number for the one that exists.

Does a temporary table exist in this session?

SELECT OBJECT_ID(N'tempdb..#work') AS work_table_id;
What to look for

NULL means the temporary table is not visible here: it was created in an inner scope that has ended, in another session, or not at all.

How to fix it

Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.

Use the real schema and name

Changes schema

Correct the two-part name using the first diagnostic. This is the change you meant to make, now pointed at the right table.

ALTER TABLE billing.Invoice WITH CHECK CHECK CONSTRAINT ALL;

Guard optional steps in deployment scripts

Changes schema

When a script must run against databases where the table may legitimately be absent, test for it first. Do not use the guard to hide a misspelt name.

IF OBJECT_ID(N'dbo.Invoice', N'U') IS NOT NULL
    ALTER TABLE dbo.Invoice ADD paid_on date NULL;

Create the temporary table in the outer scope

Changes data

A temporary table created in the calling batch is visible to dynamic SQL and procedures it calls, and it stays until the session ends or you drop it. Create it first, then fill it from the inner scope.

CREATE TABLE #work (id int);
EXEC (N'INSERT INTO #work (id) VALUES (1);');
ALTER TABLE #work ADD note varchar(10);

Examples

ALTER TABLE on a misspelt table name

CREATE TABLE dbo.Invoice (id INT PRIMARY KEY, amount DECIMAL(10,2));

ALTER TABLE dbo.Invoices ADD paid_on DATE NULL;  -- the table is Invoice
Result
Msg 4902, Level 16, State 1, Line 3
Cannot find the object "dbo.Invoices" because it does not exist or you do not have permissions.

Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.

Re-enabling constraints on a table in another schema

CREATE SCHEMA billing;
GO
CREATE TABLE billing.Invoice (id INT PRIMARY KEY, amount DECIMAL(10,2) CHECK (amount >= 0));

ALTER TABLE dbo.Invoice WITH CHECK CHECK CONSTRAINT ALL;  -- script assumes dbo
Result
Msg 4902, Level 16, State 1, Line 3
Cannot find the object "dbo.Invoice" because it does not exist or you do not have permissions.

Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.

A temporary table created inside dynamic SQL

EXEC (N'CREATE TABLE #work (id INT);');   -- #work is dropped when EXEC ends

ALTER TABLE #work ADD note VARCHAR(10);
Result
Msg 4902, Level 16, State 1, Line 3
Cannot find the object "#work" because it does not exist or you do not have permissions.

Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.

How to prevent it

Always use two-part names in DDL and check the database name at the top of deployment scripts. Generate constraint maintenance lists from sys.tables at run time rather than keeping hard-coded lists that drift.

Create temporary tables in the outermost scope that needs them. For ALTER TABLE options and how constraints are enabled again after a load, see DDL commands and SQL constraints explained.

Common questions

The table exists. Why do I still get error 4902?

Check the schema and the database first: dbo.Invoice and billing.Invoice are different objects, and a script may run in master by default. If it really is the same table, compare the spelling, including case on a case-sensitive database.

Is error 4902 a permissions problem?

The wording allows for it, but in our tests a user without ALTER permission on an existing table, with or without SELECT on it, received error 1088 instead. A 4902 almost always means the name does not resolve.

Why can I not alter a temp table created by EXEC?

A local temporary table belongs to the scope that created it and is dropped when that scope ends. A #table made inside EXEC or a procedure is gone by the time the next statement of the caller runs.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 24 October 2026; examples and diagnostics run on SQL Server 2025 (17.0.1145.4). Message text checked against the engine's sys.messages catalogue.

Still stuck on a SQL Server error?

Browse the full SQL Server error library, or get hands-on help from an experienced SQL professional.