INTERNAL Error: "Failed to bind column reference" — window-over-aggregate used both standalone and inside arithmetic in a GROUP BY projection · Issue #23383 · duckdb/duckdb · GitHub
Skip to content

INTERNAL Error: "Failed to bind column reference" — window-over-aggregate used both standalone and inside arithmetic in a GROUP BY projection #23383

Closed

Description

@lloydtabb

What happens?

In the projection of a GROUP BY query, using the same window-over-aggregate expression (MAX(SUM(n)) OVER (PARTITION BY name)) both standalone and as a sub-expression of arithmetic crashes the binder with an internal assertion instead of executing:

INTERNAL Error: Failed to bind column reference "" [19.1] (bindings: {#[22.0], #[22.1], #[20.0], #[19.0]})
... in duckdb::ColumnBindingResolver::VisitReplace ...

The same query with the window used only once, or used twice but never inside arithmetic, runs correctly — so it is specifically the duplicated window expression (one bare, one nested inside arithmetic) that fails to rebind.

To Reproduce

Failing query (CLI, no extensions, inline data):

SELECT
  name,
  MAX(SUM(n)) OVER (PARTITION BY name)          AS w1,
  SUM(n) / MAX(SUM(n)) OVER (PARTITION BY name) AS w2
FROM (VALUES ('a',1),('a',2),('b',3)) t(name, n)
GROUP BY name;

Output:

INTERNAL Error: Failed to bind column reference "" [19.1] (bindings: {#[22.0], #[22.1], #[20.0], #[19.0]})

This error signals an assertion failure within DuckDB. This usually occurs due to unexpected conditions or errors in the program's logic.
For more information, see https://duckdb.org/docs/stable/dev/internal_errors

(Stack trace top frames: ColumnBindingResolver::VisitReplace -> LogicalOperatorVisitor::VisitExpression -> ... -> PhysicalPlanGenerator::ResolveAndPlan.)

Negative controls — both succeed:

-- (1) window used once, inside arithmetic only:
SELECT name, SUM(n) / MAX(SUM(n)) OVER (PARTITION BY name) AS w2
FROM (VALUES ('a',1),('a',2),('b',3)) t(name,n)
GROUP BY name;

-- (2) window used twice but neither occurrence inside arithmetic:
SELECT name,
  MAX(SUM(n)) OVER (PARTITION BY name) AS w1,
  MAX(SUM(n)) OVER (PARTITION BY name) AS w2
FROM (VALUES ('a',1),('a',2),('b',3)) t(name,n)
GROUP BY name;

The bug reproduces across versions: CLI v1.5.2 (Variegata) 8a5851971f, @duckdb/node-bindings 1.5.3-r.2, and @duckdb/node-api 1.5.4-r.1 (latest stable at time of filing). It is therefore not a recent regression.

(Found while reducing SQL generated by Malloy for an "ungrouped aggregate" / level-of-detail query, but the reproduction above is plain SQL with no Malloy or external dependency.)

OS:

Debian GNU/Linux 12 (bookworm), aarch64 (Linux 6.6.99)

DuckDB Version:

1.5.2 (also reproduced on 1.5.3 and 1.5.4)

DuckDB Client:

CLI (also reproduced via Node.js @duckdb/node-api)

Hardware:

No response

Full Name:

Lloyd Tabb

Affiliation:

Malloy

Did you include all relevant configuration (e.g., CPU architecture, Linux distribution) to reproduce the issue?

  • Yes, I have

Did you include all code required to reproduce the issue?

  • Yes, I have

Did you include all relevant data sets for reproducing the issue?

Not applicable - the reproduction does not require a data set

Activity

  1. krlmlr commented on Jun 20, 2026

    @krlmlr
    Contributor

    Similar reprex, not sure if it's the same issue:

    COPY (SELECT 'a' AS g) TO 'min.parquet' (FORMAT parquet);
    SELECT NULLIF(SUM(1) OVER (PARTITION BY g), 0) AS pct
    FROM (SELECT g FROM read_parquet('min.parquet') GROUP BY g) t;

    Linux only. A bisection is running, wil report results here.

    Linux results

    In a fresh ubuntu:latest container on aarch64:

    root@c9272dd9f87b:/# /root/.duckdb/cli/latest/duckdb
    DuckDB v1.5.4 (Variegata)
    Enter ".help" for usage hints.
    memory D COPY (SELECT 'a' AS g) TO 'min.parquet' (FORMAT parquet);
    memory D SELECT NULLIF(SUM(1) OVER (PARTITION BY g), 0) AS pct
             FROM (SELECT g FROM read_parquet('min.parquet') GROUP BY g) t;
    INTERNAL Error:
    Failed to bind column reference "" [18.1] (bindings: {#[21.0], #[19.0], #[18.0]})
    
    Stack Trace:
    
    /root/.duckdb/cli/latest/duckdb() [0x6e9804]
    /root/.duckdb/cli/latest/duckdb() [0x6e98c8]
    /root/.duckdb/cli/latest/duckdb() [0x6ec95c]
    /root/.duckdb/cli/latest/duckdb() [0x8c787c]
    /root/.duckdb/cli/latest/duckdb() [0x8c05f0]
    /root/.duckdb/cli/latest/duckdb() [0xea8bc0]
    /root/.duckdb/cli/latest/duckdb() [0xea7f24]
    /root/.duckdb/cli/latest/duckdb() [0xea5f20]
    /root/.duckdb/cli/latest/duckdb() [0xea64a0]
    /root/.duckdb/cli/latest/duckdb() [0x8b1528]
    /root/.duckdb/cli/latest/duckdb() [0x8b204c]
    /root/.duckdb/cli/latest/duckdb() [0x8b22b0]
    /root/.duckdb/cli/latest/duckdb() [0x97f1d4]
    /root/.duckdb/cli/latest/duckdb() [0x97fd8c]
    /root/.duckdb/cli/latest/duckdb() [0x984510]
    /root/.duckdb/cli/latest/duckdb() [0x98492c]
    /root/.duckdb/cli/latest/duckdb() [0x984d40]
    /root/.duckdb/cli/latest/duckdb() [0x985930]
    /root/.duckdb/cli/latest/duckdb() [0x988304]
    /root/.duckdb/cli/latest/duckdb() [0x988468]
    /root/.duckdb/cli/latest/duckdb() [0x98856c]
    /root/.duckdb/cli/latest/duckdb() [0x9893ac]
    /root/.duckdb/cli/latest/duckdb() [0x470de0]
    /root/.duckdb/cli/latest/duckdb() [0x471284]
    /root/.duckdb/cli/latest/duckdb() [0x471704]
    /root/.duckdb/cli/latest/duckdb() [0x47a654]
    /root/.duckdb/cli/latest/duckdb() [0x47b920]
    /root/.duckdb/cli/latest/duckdb() [0x45208c]
    /usr/lib/aarch64-linux-gnu/libc.so.6(+0x22f1c) [0xffff9e3c2f1c]
    /usr/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0x9c) [0xffff9e3c305c]
    /root/.duckdb/cli/latest/duckdb() [0x456710]
    
    This error signals an assertion failure within DuckDB. This usually occurs due to unexpected conditions or errors in the program's logic.
    For more information, see https://duckdb.org/docs/stable/dev/internal_errors

    macOS results

    ➜  ~ duckdb
    DuckDB v1.5.4 (Variegata)
    Enter ".help" for usage hints.
    memory D COPY (SELECT 'a' AS g) TO 'min.parquet' (FORMAT parquet);
    memory D SELECT NULLIF(SUM(1) OVER (PARTITION BY g), 0) AS pct
             FROM (SELECT g FROM read_parquet('min.parquet') GROUP BY g) t;
    ┌────────┐
    │  pct   │
    │ int128 │
    ├────────┤
    │      1 │
    └────────┘
  2. krlmlr commented on Jun 20, 2026

    @krlmlr
    Contributor

    Bisection suggests that #20515 is the trigger.

  3. krlmlr commented on Jun 20, 2026

    @krlmlr
    Contributor

    Confirming the two reprexes are the same problem.

  4. hawkfish commented on Jun 22, 2026

    @hawkfish
    Member

    Workaround (as you might expect...):

    statement ok
    SET disabled_optimizers = 'window_self_join';
  5. Youssef-joe commented on Jun 23, 2026

    @Youssef-joe

    Hi, I've been investigating this and found the root cause. I've opened a PR with a fix --> #23412 would love feedback.

    What's happening: The binder creates one entry in node.windows per occurrence of a window expression no deduplication. So MAX(SUM(n)) OVER (PARTITION BY name) appearing twice (once as w1, once inside the division for w2) produces two separate entries in the LogicalWindow, at indices (window_index, 0) and (window_index, 1).

    The window_self_join optimizer was then creating one aggregate expression per window expression (including those duplicates), producing two aggregate outputs: (aggregate_index, 0) and (aggregate_index, 1). It registered binding replacements for both. A later optimizer (column_lifetime) then correctly identified that (aggregate_index, 1) is a duplicate of (aggregate_index, 0) and pruned it but the replacement binding still pointed expressions at the now-missing (aggregate_index, 1), causing the crash in ColumnBindingResolver.

    Fix I tried is: Deduplicate identical window expressions when building the new aggregate inside window_self_join. If two window expressions are equal (via BoundWindowExpression::Equals), they share the same aggregate output index. Both (window_index, 0) and (window_index, 1) then map to (aggregate_index, 0) one aggregate, nothing to prune, no crash..

  6. added 2 commits that reference this issue on Jun 23, 2026
  7. lnkuiper commented on Jun 23, 2026

    @lnkuiper
    Member

    Closed via #23409

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions