Hmbown/CodeWhale · error · Error

public key required to emit the facts_key row

Error message

public key required to emit the facts_key row

What it means

emitSql generates the SQL statements that upsert the facts_key row, and it re-verifies the envelope against the caller-provided base64 public key. It throws when publicKeyB64 is empty/missing because the emitted facts_key row cannot be written without the public key, and the envelope could not be independently verified.

Solutions

  1. Pass the pinned active key's base64 public key: emitSql(envelope, { publicKeyB64: key.publicKey, ... })
  2. If you have the envelope but not the key, resolve it via loadTrustedKeysFromRepo/activePublishingKey first, then hand key.publicKey to emitSql
  3. Confirm the keys.ts parse succeeded upstream so publicKey is not undefined

Example fix

// before
emitSql(envelope, { publishedBy: "founder" })
// after
emitSql(envelope, { publishedBy: "founder", publicKeyB64: key.publicKey })
Defensive patterns

Strategy: validation

Validate before calling

function emitSqlGuarded(envelope, opts) {
  if (!opts?.publicKeyB64) throw new Error("emitSql requires publicKeyB64 (the pinned active key's base64 public key)");
  return emitSql(envelope, opts);
}

Type guard

function hasPublicKey(opts) {
  return typeof opts === "object" && opts !== null && typeof opts.publicKeyB64 === "string" && opts.publicKeyB64.length > 0;
}

Try / catch

try {
  const sql = emitSql(envelope, opts);
} catch (err) {
  if (err.message === "public key required to emit the facts_key row") {
    console.error("Pass the pinned active key's publicKey (e.g. from activePublishingKey(...).key.publicKey)");
    process.exit(1);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling emitSql(envelope, opts) with opts.publicKeyB64 undefined, null, or an empty string — e.g. the options object was omitted, or the key was destructured from a source that lacked it.

Common situations: A script invoked emitSql with only publishedBy/notes, forgetting the public key; the active key's publicKey field was undefined because key parsing failed upstream; refactored call site dropped the option.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/fc826bcda3a019aa. Report an issue: GitHub.

Appendix: source

Thrown at web/scripts/facts-publish.mjs:427

      (check.payload.not_after != null && utcTime(check.payload.not_after) <= now)) throw new Error("publication timestamp is future or expired");
  return { key, check };
}

function refuseUnderCi() {
  for (const marker of CI_MARKERS) {
    if (process.env[marker] && !/^(0|false|no|off)$/i.test(process.env[marker])) {
      throw new Error(`refusing to run with a secret under CI (${marker} is set); publish from the founder's machine`);
    }
  }
}

function sqlLiteral(value) {
  if (value === null || value === undefined) return "null";
  return `'${String(value).replace(/'/g, "''")}'`;
}

export function emitSql(envelope, { publishedBy = "", publicKeyB64, notes = "" }) {
  if (!publicKeyB64) throw new Error("public key required to emit the facts_key row");
  const check = verifyEnvelope(envelope, publicKeyB64);
  if (!check.ok) throw new Error(`envelope does not verify: ${check.errors.join("; ")}`);
  const payloadJson = Buffer.from(envelope.payload_b64, "base64").toString("utf8");
  return [
    "begin;",
    `insert into public.facts_key (key_id, scope, algorithm, public_key, status)`,
    `  values (${sqlLiteral(envelope.key_id)}, 'global', 'ed25519', ${sqlLiteral(publicKeyB64)}, 'active')`,
    `  on conflict (key_id) do nothing;`,
    `insert into public.facts_release (channel_id, facts_version, schema_version, envelope_version, applies_to, key_id, payload_b64, sig_b64, sigs, payload, published_at, not_after, published_by, notes)`,
    `  select c.id, ${envelope.facts_version}, ${envelope.schema_version}, ${envelope.envelope}, ${sqlLiteral(envelope.applies_to)}, ${sqlLiteral(envelope.key_id)},`,
    `         ${sqlLiteral(envelope.payload_b64)}, ${sqlLiteral(envelope.sig_b64)}, ${sqlLiteral(JSON.stringify(envelope.sigs ?? []))}::jsonb,`,
    `         ${sqlLiteral(payloadJson)}::jsonb, ${sqlLiteral(envelope.published_at)}::timestamptz, ${sqlLiteral(check.payload.not_after ?? null)}::timestamptz,`,
    `         ${sqlLiteral(publishedBy)}, ${sqlLiteral(notes)}`,
    `    from public.facts_channel c where c.scope = 'global' and c.slug = ${sqlLiteral(envelope.channel)};`,
    "commit;",
    "",
  ].join("\n");
}

View on GitHub (pinned to 433685b202)