nanocoai/nanoclaw · error

--${column.name.replace(/_/g, '-')} must be true or false

Error message

--${column.name.replace(/_/g, '-')} must be true or false

What it means

Thrown by coerceListFilter in src/cli/crud.ts when a boolean column filter receives a value that is not one of true/false/'true'/'false'/'1'/'0'/1/0. The generic list handler coerces CLI filter flags into SQLite-storable 0/1 integers, and any other string (e.g. 'yes', 'on') is rejected. This protects boolean columns from being compared against non-boolean text.

Source

Thrown at src/cli/crud.ts:178

// ---------------------------------------------------------------------------
// Generic SQL handlers
// ---------------------------------------------------------------------------

function visibleColumns(def: ResourceDef): string[] {
  return def.columns.map((c) => c.name);
}

function coerceListFilter(column: ColumnDef, value: unknown): unknown {
  switch (column.type) {
    case 'number': {
      const number = Number(value);
      if (Number.isNaN(number)) throw new Error(`--${column.name.replace(/_/g, '-')} must be a number`);
      return number;
    }
    case 'boolean':
      if (value === true || value === 'true' || value === '1' || value === 1) return 1;
      if (value === false || value === 'false' || value === '0' || value === 0) return 0;
      throw new Error(`--${column.name.replace(/_/g, '-')} must be true or false`);
    case 'json':
      return typeof value === 'string' ? value : JSON.stringify(value);
    case 'string':
      return String(value);
  }
}

function listOrder(def: ResourceDef): string {
  if (def.listOrder) return def.listOrder;
  const timestamp = def.columns.find((column) => column.name.endsWith('_at'))?.name;
  return timestamp ? `${timestamp} DESC, ${def.idColumn}` : def.idColumn;
}

function genericList(def: ResourceDef) {
  const cols = visibleColumns(def).join(', ');
  const filterableColumns = new Map(def.columns.filter((c) => !c.generated).map((c) => [c.name, c]));
  return async (args: Record<string, unknown>) => {
    const limit = args.limit !== undefined ? Math.max(1, Number(args.limit)) : 200;

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Use exactly true or false (also accepted: 1/0, 'true'/'false')
  2. Check the column definition in src/cli/resources/<resource>.ts to confirm the column is actually boolean
  3. Quote the value in shells that mangle bare true/false

Example fix

# before
ncl groups list --is-default yes
# after
ncl groups list --is-default true
Defensive patterns

Strategy: validation

Validate before calling

const BOOL_OK = new Set(['true','false','1','0',true,false,1,0]);
function assertBoolFlag(name: string, v: unknown) {
  if (!BOOL_OK.has(v as any)) throw new Error(`${name} must be true or false`);
}

Type guard

function isBoolFilter(v: unknown): boolean {
  return [true,false,'true','false','1','0',1,0].includes(v as any);
}

Prevention

When it happens

Trigger: Running `ncl <resource> list --<boolean-column> yes` or `--<boolean-column> on/off`, or any list filter where the resource defines a boolean column (e.g. is_default, enabled) and the caller passes an unrecognized truthiness spelling.

Common situations: Scripts assuming 'yes'/'no' or 'on'/'off' work as booleans; passing an empty string; passing 'True' with a capital T.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/42567196f2fa50b8. Report an issue: GitHub.