{"record":{"id":"3441b383908e1f40","repo":"paperclipai/paperclip","slug":"ctx-db-query-cannot-read-schema-ref-schema","errorCode":null,"errorMessage":"ctx.db.query cannot read schema \"${ref.schema}\"","messagePattern":"ctx\\.db\\.query cannot read schema \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/plugin-database.ts","lineNumber":272,"sourceCode":"  }\n  const statement = statements[0]!;\n  assertNoBannedSql(statement);\n  const normalized = normaliseSql(statement);\n  if (!normalized.startsWith(\"select \") && !normalized.startsWith(\"with \")) {\n    throw new Error(\"ctx.db.query only allows SELECT statements\");\n  }\n  if (/\\b(insert|update|delete|alter|create|drop|truncate)\\b/.test(normalized)) {\n    throw new Error(\"ctx.db.query cannot contain mutation or DDL keywords\");\n  }\n\n  const allowedCoreReadTables = new Set(coreReadTables);\n  for (const ref of extractQualifiedRefs(statement)) {\n    if (ref.schema === namespace) continue;\n    if (ref.schema === \"public\") {\n      assertAllowedPublicRead(ref, allowedCoreReadTables);\n      continue;\n    }\n    throw new Error(`ctx.db.query cannot read schema \"${ref.schema}\"`);\n  }\n}\n\nexport function validatePluginRuntimeExecute(query: string, namespace: string): void {\n  const statements = splitSqlStatements(query);\n  if (statements.length !== 1) {\n    throw new Error(\"Plugin runtime SQL must contain exactly one statement\");\n  }\n  const statement = statements[0]!;\n  assertNoBannedSql(statement);\n  const normalized = normaliseSql(statement);\n  if (!/^(insert\\s+into|update|delete\\s+from)\\b/.test(normalized)) {\n    throw new Error(\"ctx.db.execute only allows INSERT, UPDATE, or DELETE\");\n  }\n  if (/\\b(alter|create|drop|truncate)\\b/.test(normalized)) {\n    throw new Error(\"ctx.db.execute cannot contain DDL keywords\");\n  }\n","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/paperclipai/paperclip/blob/120ae5428fa29bee300bcf806491cd4d965fbb7c/server/src/services/plugin-database.ts#L254-L290","documentation":"Schema isolation guard for runtime reads: a qualified reference in the SELECT targets a schema outside the plugin namespace and outside the whitelisted public core tables. Plugins can only read their own tables plus approved core tables; the cross-schema read is at fault.","triggerScenarios":"Thrown at server/src/services/plugin-database.ts:272 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Query only schemas the plugin is allowed to read (its own namespace or whitelisted tables)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"120ae5428fa29bee300bcf806491cd4d965fbb7c","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}