ReviewOS

stacks/bun-query-builder

select() crashes with TypeError when given a bare string instead of an array

#1012
Closed glennmichael123 opened this 22 days ago · 0 comments
22 days ago

Summary

.select('col') (single string argument) crashes with TypeError: columns2.join is not a function at runtime instead of either accepting the string or throwing a typed validation error. The array form .select(['col']) works correctly.

Reproducer

import { db } from '@stacksjs/database' // proxies bun-query-builder

// Crashes
await db.selectFrom('judge_reviews_likes')
  .select('judge_review_id')
  .where('user_id', '=', 1)
  .execute()

// Works
await db.selectFrom('judge_reviews_likes')
  .select(['judge_review_id'])
  .where('user_id', '=', 1)
  .execute()

Error

TypeError: columns2.join is not a function. (In 'columns2.join(", ")', 'columns2.join' is undefined)
    at select (bun-query-builder/dist/src/index.js:12852:41)

Root cause

Looking at the bundled dist around the crash site, the select(columns2) implementation does:

select(columns2) {
  if (!columns2 || columns2.length === 0)
    return this;
  const fromIndex = text.indexOf(' FROM ');
  if (fromIndex !== -1) {
    text = `SELECT ${columns2.join(', ')}${text.substring(fromIndex)}`;
  } else {
    text = `SELECT ${columns2.join(', ')} FROM ${table}`;
  }
  return this;
}

The guard checks .length (a property both strings and arrays carry), but then unconditionally calls .join(', ') (only arrays have this). A bare string passes the guard and crashes on the next line.

Why it matters

Most query builders in this ecosystem (Kysely, Knex, Drizzle) accept both .select('col') and .select(['col']). The current shape silently accepts strings at compile time (the as any casts most codebases use mean TypeScript doesn't catch this) but fails at runtime with a confusing TypeError rather than a clear API error.

Suggested fix

Normalize the argument at the top of the method:

select(columns2) {
  if (!columns2) return this;
  const cols = Array.isArray(columns2) ? columns2 : [columns2];
  if (cols.length === 0) return this;
  // ...rest unchanged, using `cols.join(', ')`
}

That gives Kysely-style ergonomics for free and keeps the array form working unchanged.

If single-string is intentionally unsupported, the guard should at minimum throw a typed error (select() expects an array of column names) so the failure mode is obvious.

Workaround in the meantime

Wrap single columns in an array: .select(['col']) — works, no other side effects.

Environment

  • bun-query-builder vendored via stacks pantry (pantry/bun-query-builder)
  • Bun 1.3.x
  • SQLite driver

Caught while implementing a "people find this helpful" feature on a Stacks app — the lookup SELECT judge_review_id FROM judge_reviews_likes WHERE user_id = ? AND judge_review_id IN (...) 500'd until the column list was wrapped.

Sign in to comment on this issue.