Hmbown/CodeWhale · error · Error
channel does not exist
Error message
channel ${envelope.channel} does not exist What it means
Before inserting the release row, publish queries facts_channel filtered to scope=eq.global and slug=eq.<channel>. If the query returns an empty array, the channel does not exist in the database and publication aborts with this error. Channels must be created (via the emit-sql SQL or manually in Supabase) before publishing releases against them.
Solutions
- Create the channel row: use the emit-sql subcommand output or insert into facts_channel (scope='global', slug='<channel>')
- Check the DB: select id,slug,scope from facts_channel; confirm slug and scope='global' match envelope.channel
- Run with --dry-run to print the channel being published and compare to the DB
- Verify SUPABASE_URL points at the project that actually contains the channel
Example fix
-- before publish
-- after: create the channel first
insert into facts_channel (slug, scope) values ('beta', 'global'); Defensive patterns
Strategy: try-catch
Validate before calling
const channels = await postgrest(`facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(channel)}&select=id`);
if (!channels?.length) throw new Error(`channel ${channel} missing; create it before publishing`); Try / catch
try {
await run(['publish', envelopePath]);
} catch (e) {
if (e.message.includes('does not exist')) {
console.error(`Create the channel first: insert into facts_channel (slug, scope) values ('${envelope.channel}', 'global');`);
}
throw e;
} Prevention
- Provision channels (via emit-sql) before first publish
- Verify slug and scope='global' match the envelope exactly
- Confirm SUPABASE_URL targets the project containing the channel
- Use --dry-run to echo the channel being published
When it happens
Trigger: Publishing an envelope whose channel slug has no matching global facts_channel row; --dry-run passing (no DB check) then real publish failing; a new channel like "beta" that was never provisioned; slug case/typo mismatch between envelope.channel and the DB row.
Common situations: First publication to a brand-new channel; rotated/renamed channels where the DB still has the old slug; publishing against the wrong Supabase project (env var pointing elsewhere); a channel created with non-global scope so the scope=eq.global filter misses it.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Another pet recorder is using this output.
- channel does not exist
- failed to enable WAL for
- invalid-channel
- invalid Supabase endpoint
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/3b39ceb624a907f2.
Report an issue: GitHub.
Appendix: source
Thrown at web/scripts/facts-publish.mjs:595
schema_version: envelope.schema_version,
envelope_version: envelope.envelope,
applies_to: envelope.applies_to,
key_id: envelope.key_id,
payload_b64: envelope.payload_b64,
sig_b64: envelope.sig_b64,
sigs: envelope.sigs ?? [],
payload: check.payload,
published_at: envelope.published_at,
not_after: check.payload.not_after ?? null,
published_by: String(flags["published-by"] ?? ""),
notes: String(flags.notes ?? ""),
};
if (flags["dry-run"]) {
console.log(JSON.stringify({ dry_run: true, channel: envelope.channel, facts_key: { key_id: envelope.key_id, public_key: pub }, facts_release: { ...row, payload_b64: `<${envelope.payload_b64.length} chars>` } }, null, 2));
return 0;
}
const channels = await postgrest(`facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(envelope.channel)}&select=id`);
if (!channels?.length) throw new Error(`channel ${envelope.channel} does not exist`);
await postgrest("facts_key", { method: "POST", body: { key_id: envelope.key_id, scope: "global", algorithm: "ed25519", public_key: pub, status: "active" }, prefer: "resolution=ignore-duplicates,return=minimal" });
const inserted = await postgrest("facts_release", { method: "POST", body: { ...row, channel_id: channels[0].id }, prefer: "return=representation" });
console.log(JSON.stringify({ published: true, channel: envelope.channel, facts_version: envelope.facts_version, release_id: inserted?.[0]?.id ?? null, payload_sha256: inserted?.[0]?.payload_sha256 ?? null }, null, 2));
return 0;
}
if (cmd === "revoke") {
const channel = String(flags.channel ?? "");
const version = Number(flags.version);
const reason = String(flags.reason ?? "");
if (!CHANNEL_RE.test(channel) || !Number.isSafeInteger(version) || version <= 0 || !reason) throw new Error("--channel, --version and --reason are required");
if (flags["dry-run"]) {
console.log(JSON.stringify({ dry_run: true, channel, facts_version: version, status: "revoked", revoke_reason: reason }, null, 2));
return 0;
}
const channels = await postgrest(`facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(channel)}&select=id`);
if (!channels?.length) throw new Error(`channel ${channel} does not exist`);
const updated = await postgrest(`facts_release?channel_id=eq.${channels[0].id}&facts_version=eq.${version}`, {
method: "PATCH",View on GitHub (pinned to 433685b202)