paperclipai/paperclip · error · Error

Unsafe SQL ${label}: ${value}

Error message

Unsafe SQL ${label}: ${value}

What it means

Generic identifier guard (assertIdentifier): the supplied string (table/column/schema identifier passed by a plugin) fails IDENTIFIER_RE, so it could be unsafe to interpolate into SQL or DDL. The offending value's role is named by label; the plugin-supplied identifier is at fault.

Source

Thrown at server/src/services/plugin-database.ts:48

export function derivePluginDatabaseNamespace(
  pluginKey: string,
  namespaceSlug?: string,
): string {
  const hash = createHash("sha256").update(pluginKey).digest("hex").slice(0, 10);
  const slug = (namespaceSlug ?? pluginKey)
    .toLowerCase()
    .replace(/[^a-z0-9_]+/g, "_")
    .replace(/^_+|_+$/g, "")
    .replace(/_+/g, "_")
    .slice(0, 36) || "plugin";
  const namespace = `plugin_${slug}_${hash}`;
  return namespace.slice(0, MAX_POSTGRES_IDENTIFIER_LENGTH);
}

function assertIdentifier(value: string, label = "identifier"): string {
  if (!IDENTIFIER_RE.test(value)) {
    throw new Error(`Unsafe SQL ${label}: ${value}`);
  }
  return value;
}

function quoteIdentifier(value: string): string {
  return `"${assertIdentifier(value).replaceAll("\"", "\"\"")}"`;
}

function splitSqlStatements(input: string): string[] {
  const statements: string[] = [];
  let start = 0;
  let quote: "'" | "\"" | null = null;
  let lineComment = false;
  let blockComment = false;

  for (let i = 0; i < input.length; i += 1) {
    const char = input[i]!;
    const next = input[i + 1];

View on GitHub (pinned to 120ae5428f)

Solutions

  1. Rewrite the plugin SQL to avoid the unsafe construct named in the message.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/plugin-database.ts:48 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/a9291a8349d0901a. Report an issue: GitHub.