{"record":{"id":"accd323c32a15e63","repo":"can1357/oh-my-pi","slug":"sqlsessionstorage-unable-to-infer-adapter-from-cl","errorCode":null,"errorMessage":"SqlSessionStorage: unable to infer adapter from client.options.adapter=${JSON.stringify(reported)}. Pass an explicit `adapter` option (\"postgres\" | \"mysql\" | \"sqlite\").","messagePattern":"SqlSessionStorage: unable to infer adapter from client\\.options\\.adapter=(.+?)\\. Pass an explicit `adapter` option \\(\"postgres\" \\| \"mysql\" \\| \"sqlite\"\\)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/sql-session-storage.ts","lineNumber":115,"sourceCode":"const DEFAULT_TABLE = \"omp_session_files\";\nconst IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]{0,62}$/;\nconst utf8Decoder = new TextDecoder(\"utf-8\");\n\nfunction enoent(p: string): NodeJS.ErrnoException {\n\tconst err = new Error(`ENOENT: no such file, '${p}'`) as NodeJS.ErrnoException;\n\terr.code = \"ENOENT\";\n\terr.errno = -2;\n\terr.path = p;\n\terr.syscall = \"open\";\n\treturn err;\n}\n\nfunction detectAdapter(client: SqlSessionStorageClient): SqlSessionStorageAdapter {\n\tconst reported = String(client.options?.adapter ?? \"\").toLowerCase();\n\tif (reported === \"postgres\" || reported === \"postgresql\" || reported === \"pg\") return \"postgres\";\n\tif (reported === \"mysql\" || reported === \"mariadb\") return \"mysql\";\n\tif (reported === \"sqlite\" || reported === \"sqlite3\") return \"sqlite\";\n\tthrow new Error(\n\t\t`SqlSessionStorage: unable to infer adapter from client.options.adapter=${JSON.stringify(reported)}. ` +\n\t\t\t`Pass an explicit \\`adapter\\` option (\"postgres\" | \"mysql\" | \"sqlite\").`,\n\t);\n}\n\nfunction buildQueries(adapter: SqlSessionStorageAdapter, table: string): DialectQueries {\n\tconst placeholder = adapter === \"postgres\" ? (n: number): string => `$${n}` : (_n: number): string => \"?\";\n\n\tif (adapter === \"mysql\") {\n\t\treturn {\n\t\t\tcreateTable:\n\t\t\t\t`CREATE TABLE IF NOT EXISTS ${table} (` +\n\t\t\t\t`path VARCHAR(512) NOT NULL PRIMARY KEY, ` +\n\t\t\t\t`content LONGTEXT NOT NULL, ` +\n\t\t\t\t`mtime_ms BIGINT NOT NULL, ` +\n\t\t\t\t`title TEXT NULL, ` +\n\t\t\t\t`title_source VARCHAR(16) NULL, ` +\n\t\t\t\t`title_updated_at VARCHAR(64) NULL` +","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/sql-session-storage.ts#L97-L133","documentation":"SqlSessionStorage wraps an existing SQL client (e.g. drizzle/kysely-style) and needs to know which SQL dialect to generate queries for. When no explicit `adapter` option is passed, detectAdapter inspects `client.options.adapter` and throws if the reported value isn't one of postgres/mysql/sqlite (aliases like 'pg', 'mariadb', 'sqlite3' accepted). Fail-closed by design: guessing the dialect could produce broken SQL.","triggerScenarios":"Constructing SqlSessionStorage with `{ client }` where client.options.adapter is undefined, empty, or an unrecognized string (sql-session-storage.ts:108-117).","commonSituations":"Passing a raw database driver that doesn't expose an options.adapter field; typo like 'postgresSQL' or 'psql'; using a client library whose adapter config lives somewhere else (e.g. client.config.adapter); forgetting the adapter option after switching client libraries.","solutions":["Pass the adapter explicitly: new SqlSessionStorage({ client, adapter: 'postgres' | 'mysql' | 'sqlite' }).","If relying on inference, ensure the client object exposes options.adapter with a recognized value ('postgres', 'postgresql', 'pg', 'mysql', 'mariadb', 'sqlite', 'sqlite3').","Check for typos/case — detection lowercases, but the spelling must match exactly.","Log/inspect JSON.stringify(client.options) to see what the detector actually sees."],"exampleFix":"// before\nconst storage = new SqlSessionStorage({ client: pgClient });\n// after\nconst storage = new SqlSessionStorage({ client: pgClient, adapter: 'postgres' });","handlingStrategy":"validation","validationCode":"const ADAPTERS = new Set(['postgres', 'postgresql', 'pg', 'mysql', 'mariadb', 'sqlite', 'sqlite3']);\nif (!ADAPTERS.has(String(client.options?.adapter ?? '').toLowerCase())) {\n  throw new Error('pass an explicit adapter option to SqlSessionStorage');\n}","typeGuard":"type KnownAdapter = 'postgres' | 'mysql' | 'sqlite';\nfunction hasKnownAdapter(c: unknown): c is { options: { adapter: KnownAdapter } } {\n  const a = (c as { options?: { adapter?: string } })?.options?.adapter;\n  return ['postgres', 'mysql', 'sqlite'].includes(String(a).toLowerCase());\n}","tryCatchPattern":"try {\n  storage = new SqlSessionStorage({ client });\n} catch (err) {\n  if (String(err.message).startsWith('SqlSessionStorage: unable to infer adapter')) {\n    storage = new SqlSessionStorage({ client, adapter: detectFromEnv() });\n  } else throw err;\n}","preventionTips":["Always pass `adapter` explicitly instead of relying on inference.","Centralize the adapter value in one config constant.","Validate driver config at startup with a fail-fast check."],"tags":["sql","configuration","adapter","validation"],"backgroundTag":"unable-to-infer-adapter","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}