{"record":{"id":"3b39ceb624a907f2","repo":"Hmbown/CodeWhale","slug":"channel-envelope-channel-does-not-exist","errorCode":null,"errorMessage":"channel ${envelope.channel} does not exist","messagePattern":"channel (.+?) does not exist","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/scripts/facts-publish.mjs","lineNumber":595,"sourceCode":"      schema_version: envelope.schema_version,\n      envelope_version: envelope.envelope,\n      applies_to: envelope.applies_to,\n      key_id: envelope.key_id,\n      payload_b64: envelope.payload_b64,\n      sig_b64: envelope.sig_b64,\n      sigs: envelope.sigs ?? [],\n      payload: check.payload,\n      published_at: envelope.published_at,\n      not_after: check.payload.not_after ?? null,\n      published_by: String(flags[\"published-by\"] ?? \"\"),\n      notes: String(flags.notes ?? \"\"),\n    };\n    if (flags[\"dry-run\"]) {\n      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));\n      return 0;\n    }\n    const channels = await postgrest(`facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(envelope.channel)}&select=id`);\n    if (!channels?.length) throw new Error(`channel ${envelope.channel} does not exist`);\n    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\" });\n    const inserted = await postgrest(\"facts_release\", { method: \"POST\", body: { ...row, channel_id: channels[0].id }, prefer: \"return=representation\" });\n    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));\n    return 0;\n  }\n  if (cmd === \"revoke\") {\n    const channel = String(flags.channel ?? \"\");\n    const version = Number(flags.version);\n    const reason = String(flags.reason ?? \"\");\n    if (!CHANNEL_RE.test(channel) || !Number.isSafeInteger(version) || version <= 0 || !reason) throw new Error(\"--channel, --version and --reason are required\");\n    if (flags[\"dry-run\"]) {\n      console.log(JSON.stringify({ dry_run: true, channel, facts_version: version, status: \"revoked\", revoke_reason: reason }, null, 2));\n      return 0;\n    }\n    const channels = await postgrest(`facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(channel)}&select=id`);\n    if (!channels?.length) throw new Error(`channel ${channel} does not exist`);\n    const updated = await postgrest(`facts_release?channel_id=eq.${channels[0].id}&facts_version=eq.${version}`, {\n      method: \"PATCH\",","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/web/scripts/facts-publish.mjs#L577-L613","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"-- before publish\n-- after: create the channel first\ninsert into facts_channel (slug, scope) values ('beta', 'global');","handlingStrategy":"try-catch","validationCode":"const channels = await postgrest(`facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(channel)}&select=id`);\nif (!channels?.length) throw new Error(`channel ${channel} missing; create it before publishing`);","typeGuard":null,"tryCatchPattern":"try {\n  await run(['publish', envelopePath]);\n} catch (e) {\n  if (e.message.includes('does not exist')) {\n    console.error(`Create the channel first: insert into facts_channel (slug, scope) values ('${envelope.channel}', 'global');`);\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["database","supabase","publishing"],"backgroundTag":"record-not-found","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}