You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On dbt-sqlserver1.10.1rc1, any incremental, table, or snapshot model whose target table has a wide index fails during materialization. The failure comes from the new index-reconciliation introspection (sqlserver__reconcile_indexes → sqlserver__describe_indexes), which concatenates each index's column names with STRING_AGG(col.[name], ', ')without casting the input to a LOB type. When an index spans enough columns that the concatenated name list exceeds 8000 bytes, SQL Server raises error 9829 and the whole model rolls back.
We hit this on a table configured with as_columnstore=true: the clustered columnstore index spans all 534 columns of the table, so the aggregated column-name list blows past the 8000-byte cap. Measured on the failing table, the index's concatenated column-name list is ~12,772 characters (~25,544 bytes as nvarchar) — roughly 3x over the limit.
This is a regression introduced with the index feature in #714 and it is still present on master.
Environment
dbt-sqlserver: 1.10.1rc1
dbt-core: 1.11.12
dbt-adapters: 1.24.2
ODBC Driver 17 for SQL Server
SQL Server 2017+ (the adapter's STRING_AGG floor)
Symptom
The model runs for its full duration (in our case ~177s — the data load actually succeeds), then errors at the very end during index reconciliation:
203 of 735 ERROR creating sql incremental model innocap.position_map_daily ..... [ERROR in 176.97s]
The summary line carries no message, so at first glance it looks like a silent failure. The real error is only visible in logs/dbt.log:
sqlserver adapter: Database error: ('42000', '[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]STRING_AGG aggregation result exceeded the limit of 8000 bytes. Use LOB types to avoid result truncation. (9829) (SQLExecDirectW)')
Root cause
sqlserver__describe_indexes (dbt/include/sqlserver/macros/adapters/indexes.sql) builds the column lists for each index using STRING_AGG(col.[name], ', '). col.[name] is sysname (nvarchar(128)), so STRING_AGG's result type is capped at 8000 bytes. There is no CAST(... AS NVARCHAR(MAX)), so as soon as one index's concatenated column names exceed that cap, SQL Server throws error 9829 and aborts.
The three affected aggregates (current master, ~lines 382 / 390 / 398; identical in 1.10.1rc1):
select string_agg(col.[name], ', ') within group (order byic.key_ordinal) as cols -- key columns
...
select string_agg(col.[name], ', ') as cols -- included columns
...
select string_agg(col.[name], ', ') as cols -- descending columns
The overflow condition is met by any table where an index's column-name list concatenates to more than 8000 bytes. The most likely trigger in practice is as_columnstore=true on a wide table: the clustered columnstore index spans every column, so a few hundred columns with realistic names is enough. A rowstore nonclustered index with a large INCLUDE list hits it the same way.
Confirmed on our failing table (the only index present is the columnstore index created by as_columnstore=true):
A clustered columnstore index has index_id = 1 and populates sys.index_columns with one row per table column, so every column of the table feeds the aggregate.
Affected code paths
sqlserver__describe_indexes is reached through sqlserver__reconcile_indexes, which runs from three materializations:
So this is not incremental-specific; any persistent-table build that reconciles indexes on a sufficiently wide table is affected.
Reproduction
Create a wide table with a clustered columnstore index, then run the aggregate the macro uses:
-- 1. a table wide enough that its column names concatenate to > 8000 bytes-- (a few hundred columns with realistic names; here we generate 500)
DECLARE @cols nvarchar(max) ='';
SELECT @cols = @cols +', col_'+ RIGHT('000'+ CAST(n ASvarchar(3)), 3)
+'_reasonably_descriptive_name int'FROM (SELECT TOP (500) ROW_NUMBER() OVER (ORDER BY (SELECT1)) -1AS n
FROMsys.all_objects) x;
EXEC('CREATE TABLE dbo.wide_cci_repro (id int'+ @cols +')');
CREATE CLUSTERED COLUMNSTORE INDEX cci_wide_repro ONdbo.wide_cci_repro;
-- 2. the failing aggregate from sqlserver__describe_indexesSELECT STRING_AGG(col.[name], ', ') AS cols
FROMsys.index_columns ic
INNER JOINsys.columns col
ONcol.object_id=ic.object_idANDcol.column_id=ic.column_idWHEREic.object_id= OBJECT_ID('dbo.wide_cci_repro');
-- Msg 9829: STRING_AGG aggregation result exceeded the limit of 8000 bytes.
The equivalent dbt-level repro: an incremental, table, or snapshot model with as_columnstore=true on a table with a few hundred columns.
Proposed fix
Cast the aggregated expression to a LOB type at all three call sites, exactly as the error message recommends. STRING_AGG then returns nvarchar(max) and no longer truncates:
- select string_agg(col.[name], ', ') within group (order by ic.key_ordinal) as cols+ select string_agg(cast(col.[name] as nvarchar(max)), ', ') within group (order by ic.key_ordinal) as cols
- select string_agg(col.[name], ', ') as cols+ select string_agg(cast(col.[name] as nvarchar(max)), ', ') as cols
(applied to the included-columns and descending-columns aggregates as well).
Notes
Introduced in #535 Postgres style indexes #714 (#535 Postgres style indexes, merged 2026-06-20), commit 7f1c3a654, which added sqlserver__describe_indexes and all three STRING_AGG calls. Still unfixed on master as of 2026-07-07.
The index macros were originally ported from dbt-fabric; if Fabric carries the same describe_indexes shape it likely has the same overflow.
Minor UX point: because the failure surfaces only in logs/dbt.log and the run-summary line is blank, this reads as a silent/opaque failure. Surfacing the adapter error on the summary line would have made this much faster to diagnose.
Summary
On
dbt-sqlserver1.10.1rc1, anyincremental,table, orsnapshotmodel whose target table has a wide index fails during materialization. The failure comes from the new index-reconciliation introspection (sqlserver__reconcile_indexes→sqlserver__describe_indexes), which concatenates each index's column names withSTRING_AGG(col.[name], ', ')without casting the input to a LOB type. When an index spans enough columns that the concatenated name list exceeds 8000 bytes, SQL Server raises error 9829 and the whole model rolls back.We hit this on a table configured with
as_columnstore=true: the clustered columnstore index spans all 534 columns of the table, so the aggregated column-name list blows past the 8000-byte cap. Measured on the failing table, the index's concatenated column-name list is ~12,772 characters (~25,544 bytes asnvarchar) — roughly 3x over the limit.This is a regression introduced with the index feature in #714 and it is still present on
master.Environment
dbt-sqlserver: 1.10.1rc1dbt-core: 1.11.12dbt-adapters: 1.24.2STRING_AGGfloor)Symptom
The model runs for its full duration (in our case ~177s — the data load actually succeeds), then errors at the very end during index reconciliation:
The summary line carries no message, so at first glance it looks like a silent failure. The real error is only visible in
logs/dbt.log:Root cause
sqlserver__describe_indexes(dbt/include/sqlserver/macros/adapters/indexes.sql) builds the column lists for each index usingSTRING_AGG(col.[name], ', ').col.[name]issysname(nvarchar(128)), soSTRING_AGG's result type is capped at 8000 bytes. There is noCAST(... AS NVARCHAR(MAX)), so as soon as one index's concatenated column names exceed that cap, SQL Server throws error 9829 and aborts.The three affected aggregates (current
master, ~lines 382 / 390 / 398; identical in 1.10.1rc1):The overflow condition is met by any table where an index's column-name list concatenates to more than 8000 bytes. The most likely trigger in practice is
as_columnstore=trueon a wide table: the clustered columnstore index spans every column, so a few hundred columns with realistic names is enough. A rowstore nonclustered index with a largeINCLUDElist hits it the same way.Confirmed on our failing table (the only index present is the columnstore index created by
as_columnstore=true):A clustered columnstore index has
index_id = 1and populatessys.index_columnswith one row per table column, so every column of the table feeds the aggregate.Affected code paths
sqlserver__describe_indexesis reached throughsqlserver__reconcile_indexes, which runs from three materializations:macros/materializations/models/incremental/incremental.sqlmacros/materializations/models/table/table_dml_refresh.sqlmacros/materializations/snapshots/snapshot.sqlSo this is not incremental-specific; any persistent-table build that reconciles indexes on a sufficiently wide table is affected.
Reproduction
Create a wide table with a clustered columnstore index, then run the aggregate the macro uses:
The equivalent dbt-level repro: an
incremental,table, orsnapshotmodel withas_columnstore=trueon a table with a few hundred columns.Proposed fix
Cast the aggregated expression to a LOB type at all three call sites, exactly as the error message recommends.
STRING_AGGthen returnsnvarchar(max)and no longer truncates:(applied to the included-columns and descending-columns aggregates as well).
Notes
#535 Postgres style indexes, merged 2026-06-20), commit7f1c3a654, which addedsqlserver__describe_indexesand all threeSTRING_AGGcalls. Still unfixed onmasteras of 2026-07-07.dbt-fabric; if Fabric carries the samedescribe_indexesshape it likely has the same overflow.logs/dbt.logand the run-summary line is blank, this reads as a silent/opaque failure. Surfacing the adapter error on the summary line would have made this much faster to diagnose.