{"record":{"id":"abe229455df5904d","repo":"paperclipai/paperclip","slug":"plugin-sql-cannot-mutate-or-define-objects-in-publ","errorCode":null,"errorMessage":"Plugin SQL cannot mutate or define objects in public.${ref.table}","messagePattern":"Plugin SQL cannot mutate or define objects in public\\.(.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/plugin-database.ts","lineNumber":166,"sourceCode":"        refs.push({ keyword: match[1]!.toLowerCase(), schema: match[2]!, table: match[3]! });\n      } else {\n        refs.push({ keyword: mapping.keyword, schema: match[1]!, table: match[2]! });\n      }\n    }\n  }\n  return refs;\n}\n\nfunction assertAllowedPublicRead(\n  ref: SqlRef,\n  allowedCoreReadTables: ReadonlySet<string>,\n): void {\n  if (ref.schema !== \"public\") return;\n  if (!allowedCoreReadTables.has(ref.table)) {\n    throw new Error(`Plugin SQL references public.${ref.table}, which is not whitelisted`);\n  }\n  if (![\"from\", \"join\", \"references\"].includes(ref.keyword)) {\n    throw new Error(`Plugin SQL cannot mutate or define objects in public.${ref.table}`);\n  }\n}\n\nfunction assertNoBannedSql(statement: string): void {\n  const normalized = normaliseSql(statement);\n  const banned = [\n    /\\bcreate\\s+extension\\b/,\n    /\\bcreate\\s+(?:event\\s+)?trigger\\b/,\n    /\\bcreate\\s+(?:or\\s+replace\\s+)?function\\b/,\n    /\\bcreate\\s+language\\b/,\n    /\\bgrant\\b/,\n    /\\brevoke\\b/,\n    /\\bsecurity\\s+definer\\b/,\n    /\\bcopy\\b/,\n    /\\bcall\\b/,\n    /\\bdo\\s+(?:\\$\\$|language\\b)/,\n  ];\n  const matched = banned.find((pattern) => pattern.test(normalized));","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/paperclipai/paperclip/blob/120ae5428fa29bee300bcf806491cd4d965fbb7c/server/src/services/plugin-database.ts#L148-L184","documentation":"Companion guard to the public-read whitelist: the whitelisted public table is referenced with a mutating/defining keyword (anything beyond from/join/references), so the plugin would write to or define objects on core tables. The plugin statement's use of the table is at fault.","triggerScenarios":"Thrown at server/src/services/plugin-database.ts:166 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Remove mutations/DDL against the public table from the plugin SQL; plugin SQL cannot change public schema objects."],"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"}