{"record":{"id":"f91c57f7e053252e","repo":"nanocoai/nanoclaw","slug":"column-name-replace-g-must-be-a-numb","errorCode":null,"errorMessage":"--${column.name.replace(/_/g, '-')} must be a number","messagePattern":"--(.+?) must be a number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli/crud.ts","lineNumber":172,"sourceCode":"}\n\nexport function getResource(plural: string): ResourceDef | undefined {\n  return resources.get(plural);\n}\n\n// ---------------------------------------------------------------------------\n// Generic SQL handlers\n// ---------------------------------------------------------------------------\n\nfunction visibleColumns(def: ResourceDef): string[] {\n  return def.columns.map((c) => c.name);\n}\n\nfunction coerceListFilter(column: ColumnDef, value: unknown): unknown {\n  switch (column.type) {\n    case 'number': {\n      const number = Number(value);\n      if (Number.isNaN(number)) throw new Error(`--${column.name.replace(/_/g, '-')} must be a number`);\n      return number;\n    }\n    case 'boolean':\n      if (value === true || value === 'true' || value === '1' || value === 1) return 1;\n      if (value === false || value === 'false' || value === '0' || value === 0) return 0;\n      throw new Error(`--${column.name.replace(/_/g, '-')} must be true or false`);\n    case 'json':\n      return typeof value === 'string' ? value : JSON.stringify(value);\n    case 'string':\n      return String(value);\n  }\n}\n\nfunction listOrder(def: ResourceDef): string {\n  if (def.listOrder) return def.listOrder;\n  const timestamp = def.columns.find((column) => column.name.endsWith('_at'))?.name;\n  return timestamp ? `${timestamp} DESC, ${def.idColumn}` : def.idColumn;\n}","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/cli/crud.ts#L154-L190","documentation":"The walk counts every entry (files AND directories) toward MAX_PLUGIN_FILES; exceeding it throws. Empty-dir breadth bombs trip the same bound as file bombs.","triggerScenarios":"A plugin tree with more than MAX_PLUGIN_FILES total dirents across all directories.","commonSituations":"Committed node_modules, .git directories, build artifacts, or cache folders inside a template; generated assets ballooning file counts.","solutions":["Remove node_modules/.git/build output from the plugin directory","Prune generated or cache directories before packaging","Check MAX_PLUGIN_FILES in plugin-dir.ts for the exact cap and audit with 'find <dir> | wc -l'"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let count = 0;\nconst walk = (d: string) => {\n  for (const e of fs.readdirSync(d, { withFileTypes: true })) {\n    count += 1;\n    if (e.isDirectory()) walk(path.join(d, e.name));\n  }\n};\nwalk(dir);\nif (count > MAX_PLUGIN_FILES) { /* prune (node_modules, .git, caches) before walkPluginDir */ }","typeGuard":null,"tryCatchPattern":"try { walkPluginDir(dir); } catch (e) { if (e instanceof Error && e.message.includes('more than') && e.message.includes('entries')) { /* clean the tree and retry */ } else throw e; }","preventionTips":["Audit entry counts with 'find dir | wc -l' before publishing","Keep templates minimal: no .git, node_modules, or generated caches"],"tags":["security","filesystem","limits","templates"],"backgroundTag":"resource-limit-exceeded","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}