{"record":{"id":"1ed6b2d50d8edac1","repo":"tursodatabase/turso","slug":"expected-first-argument-to-be-an-array-of-statemen-1ed6b2","errorCode":null,"errorMessage":"Expected first argument to be an array of statements","messagePattern":"Expected first argument to be an array of statements","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/compat.ts","lineNumber":330,"sourceCode":"    } catch (error: any) {\n      if (error instanceof LibsqlError) {\n        throw error;\n      }\n      throw mapDatabaseError(error, \"EXECUTE_ERROR\");\n    } finally {\n      this.execLock.release();\n    }\n  }\n\n  async batch(stmts: Array<InStatement>, options?: TransactionMode | BatchOptions): Promise<Array<BatchResultSet>> {\n    await this.execLock.acquire();\n    try {\n      if (this._closed) {\n        throw new LibsqlError(\"Client is closed\", \"CLIENT_CLOSED\");\n      }\n\n      if (!Array.isArray(stmts)) {\n        throw new TypeError(\"Expected first argument to be an array of statements\");\n      }\n\n      const { mode, raw } = this.normalizeBatchOptions(options);\n      const batchMode = mode ?? \"deferred\";\n\n      const results = await this.session.batch(\n        stmts,\n        batchMode,\n        undefined,\n        this._defaultSafeIntegers,\n        raw,\n      );\n\n      return results.map((result: any) => this.convertBatchResult(result));\n    } catch (error: any) {\n      if (error instanceof LibsqlError) {\n        throw error;\n      }","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/serverless/javascript/src/compat.ts#L312-L348","documentation":"batch() on the compatibility-layer client validates that its first argument is an array and throws a plain TypeError otherwise. This runs after the closed-client check but before any options normalization, so it fires synchronously (inside the async method) regardless of database state. It mirrors better-sqlite3/libsql-style batch APIs, which only accept arrays of statements.","triggerScenarios":"Passing a single statement object instead of an array: client.batch({ sql: 'SELECT 1' }). Passing undefined, a string, a generator, or a promise-of-array (forgetting await on a function that builds the array). Passing a Map or other array-like that fails Array.isArray.","commonSituations":"Refactoring execute(stmt) calls to batch() and forgetting to wrap the single statement in brackets; dynamically built statement lists where an empty branch yields undefined; TypeScript types bypassed with any so the compiler never flags it.","solutions":["Wrap the statement(s) in an array: client.batch([{ sql: 'SELECT 1' }])","If a variable list may be a single statement, normalize first: const stmts = Array.isArray(x) ? x : [x]","Add explicit TypeScript types (InStatement[]) at the call site so the compiler catches this before runtime","Check for a missing await when the statements come from an async builder"],"exampleFix":"// before\nawait client.batch({ sql: 'INSERT INTO t VALUES (?)', args: [1] });\n// TypeError: Expected first argument to be an array of statements\n\n// after\nawait client.batch([{ sql: 'INSERT INTO t VALUES (?)', args: [1] }]);","handlingStrategy":"type-guard","validationCode":"function toStmtArray(stmts: InStatement | InStatement[] | undefined): InStatement[] {\n  if (stmts === undefined) return [];\n  return Array.isArray(stmts) ? stmts : [stmts];\n}\n\nawait client.batch(toStmtArray(input));","typeGuard":"function isStatementArray(v: unknown): v is InStatement[] {\n  return Array.isArray(v) && v.every((s) => typeof s === 'string' || (typeof s === 'object' && s !== null && 'sql' in s));\n}","tryCatchPattern":"try {\n  await client.batch(stmts);\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('array of statements')) {\n    throw new TypeError('client.batch expects an array — wrap single statements in [ ]');\n  }\n  throw e;\n}","preventionTips":["Always type statement lists as Array<InStatement> so the compiler rejects non-arrays","Wrap single statements in brackets when copying from execute() calls","Await async builders that produce statement lists before passing them","Unit-test dynamic batch construction paths with empty, single, and multiple statements"],"tags":["validation","typeerror","batch","api-misuse"],"backgroundTag":"invalid-argument-type","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}