- Error number
- 4104
- 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
- Category
- Syntax & Object Resolution
- Last reviewed
- 23 October 2026
On this page
What it means
Error 4104 means a name with a prefix, such as c.name or Customers.name, points at a table or alias that is not available at that point in the query.
A multi-part identifier is a column written with a prefix: alias.column, table.column or schema.table.column. While compiling the query, SQL Server binds each prefix to a table or alias in the FROM clause that is in scope. If the prefix matches nothing, the batch fails with 4104 at severity 16 before any row is read.
The most common surprise: once you give a table an alias, the alias replaces the table name for the rest of the query. After FROM dbo.Customers AS c, Customers.name no longer binds; only c.name does. A plain column name that does not exist raises error 207 instead.
Common causes
- A typo in the alias, or an alias that was renamed in the FROM clause but not everywhere else.
- Using the table name after giving the table an alias.
- An ON clause or WHERE clause that refers to a table joined later, or not joined at all.
- A reference to an inner query alias from outside it, for example a derived table column used with the wrong prefix.
- An UPDATE or DELETE with a join that names the target table instead of the alias used in its FROM clause.
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.
Compile the query without running it
sp_describe_first_result_set binds the query and reports the same error, without executing it or touching data. Useful for checking a fix on a large query.
EXEC sp_describe_first_result_set
N'SELECT c.name FROM dbo.Customers AS c;'; A row per output column when the query binds; error 4104 (or 207) when a name still does not resolve.
List the columns the table really has
SELECT c.name AS column_name, t.name AS data_type
FROM sys.columns AS c
JOIN sys.types AS t ON t.user_type_id = c.user_type_id
WHERE c.object_id = OBJECT_ID(N'dbo.Customers')
ORDER BY c.column_id; The exact column names, so you can tell a wrong prefix (4104) from a wrong column name (207).
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 alias defined in the FROM clause
Read-onlyMake every prefix match an alias that exists. Once a table has an alias, use the alias everywhere.
SELECT c.name
FROM dbo.Customers AS c;
Reference only tables that are already joined
Read-onlyAn ON clause can only use tables listed before it (and the one being joined). Join the missing table, or correct the prefix.
SELECT c.name, o.total
FROM dbo.Customers AS c
JOIN dbo.Orders AS o ON o.customer_id = c.id;
In UPDATE with a join, update the alias
Changes dataWhen an UPDATE has its own FROM clause with aliases, name the alias after UPDATE so the target binds to the same table.
UPDATE c
SET c.vip = 1
FROM dbo.Customers AS c
JOIN dbo.Orders AS o ON o.customer_id = c.id
WHERE o.total > 1000;
Examples
An alias that does not exist
CREATE TABLE dbo.Customers (id INT PRIMARY KEY, name NVARCHAR(100));
SELECT c.name
FROM dbo.Customers AS cu; -- the alias is cu, not c
Msg 4104, Level 16, State 1, Line 3 The multi-part identifier "c.name" could not be bound.
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
The table name used after an alias
CREATE TABLE dbo.Customers (id INT PRIMARY KEY, name NVARCHAR(100));
SELECT Customers.name
FROM dbo.Customers AS c; -- after AS c, only c.name binds
Msg 4104, Level 16, State 1, Line 3 The multi-part identifier "Customers.name" could not be bound.
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
An ON clause that names a table not in the query
CREATE TABLE dbo.Customers (id INT PRIMARY KEY, name NVARCHAR(100));
CREATE TABLE dbo.Orders (id INT PRIMARY KEY, customer_id INT, total DECIMAL(10,2));
SELECT c.name, o.total
FROM dbo.Customers AS c
JOIN dbo.Orders AS o ON o.customer_id = x.id; -- x is not defined
Msg 4104, Level 16, State 1, Line 4 The multi-part identifier "x.id" could not be bound.
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
How to prevent it
Give every table a short, meaningful alias and prefix every column with it, especially in joins. Consistent aliases make wrong prefixes obvious in review and protect queries from ambiguous column errors (209) when tables gain columns later.
Build joins one table at a time and run the query as you go. For a refresher on join order and aliases, see the SQL joins guide.
Common questions
What is a multi-part identifier in SQL Server?
Any name written with dots, such as c.name, dbo.Customers.name or Sales.dbo.Orders.id. Error 4104 means the part before the column, the table or alias prefix, does not match anything available at that point in the query.
Why can I not use the table name after giving it an alias?
In T-SQL an alias replaces the table name for that query. After FROM dbo.Customers AS c, refer to columns as c.column; Customers.column will raise error 4104.
What is the difference between error 4104 and error 207?
Error 4104 means the prefix (table or alias) cannot be bound. Error 207 means the prefix, if any, is fine but the column name itself does not exist.
Learn more on CodeWithSQL
Official documentation
- MSSQLSERVER_4104 (Microsoft Learn)
- FROM clause plus JOIN, APPLY, PIVOT (Transact-SQL)
- sp_describe_first_result_set (Transact-SQL)
Last technically reviewed 23 October 2026; examples and diagnostics run on SQL Server 2025 (17.0.1145.4). Message text checked against the engine's sys.messages catalogue.