{"record":{"id":"146d22f30bee9532","repo":"Hmbown/CodeWhale","slug":"channel-channel-does-not-exist","errorCode":null,"errorMessage":"channel ${channel} does not exist","messagePattern":"channel (.+?) does not exist","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/scripts/facts-publish.mjs","lineNumber":611,"sourceCode":"    }\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\",\n      body: { status: \"revoked\", revoked_at: nowIso(), revoke_reason: reason },\n      prefer: \"return=representation\",\n    });\n    console.log(JSON.stringify({ revoked: updated?.length ?? 0, channel, facts_version: version }, null, 2));\n    return 0;\n  }\n  throw new Error(`unknown command ${cmd}`);\n}\n\nconst invokedDirectly = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);\nif (invokedDirectly) {\n  main(process.argv.slice(2)).then((code) => process.exit(code)).catch((err) => {\n    console.error(`facts-publish: ${err.message}`);\n    process.exit(1);\n  });\n}","sourceCodeStart":593,"sourceCodeEnd":629,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/web/scripts/facts-publish.mjs#L593-L629","documentation":"After argument validation, the revoke command queries PostgREST for a channel row in scope=global with the given slug. If the query returns an empty list, the script throws this error because revocation needs the channel's id to target facts_release rows. It is a database lookup failure, not an argument problem.","triggerScenarios":"`facts-publish revoke --channel <slug> ...` where no row exists in facts_channel with scope='global' and slug=<channel>, or the PostgREST call silently returns an empty/undefined result.","commonSituations":"Typo in the channel slug, channel created in a non-global scope, channel never published to, or environment pointing at a database (SUPABASE URL) where the channel was never seeded.","solutions":["List existing channels (e.g. via facts-publish or a direct PostgREST query) and use the exact global-scope slug","Verify SUPABASE_URL/anon key point at the environment where the channel exists","Create/seed the channel in facts_channel before revoking releases from it","Check the channel's scope is 'global', not another scope value"],"exampleFix":"// before\nnode web/scripts/facts-publish.mjs revoke --channel stabel --version 3 --reason x\n// after\nnode web/scripts/facts-publish.mjs revoke --channel stable --version 3 --reason x","handlingStrategy":"validation","validationCode":"const res = await fetch(`${SUPABASE_URL}/rest/v1/facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(channel)}&select=id`, { headers: { apikey: KEY } });\nconst rows = await res.json();\nif (!rows?.length) throw new Error(`channel ${channel} does not exist before revoke`);","typeGuard":null,"tryCatchPattern":"try {\n  await publish(\"revoke\", [\"--channel\", channel, \"--version\", String(v), \"--reason\", reason]);\n} catch (e) {\n  if (/does not exist/.test(e.message)) {\n    console.error(`Channel '${channel}' not found in global scope; check slug/environment`);\n  }\n}","preventionTips":["Verify the channel exists in the target environment before revoking","Keep a list of valid slugs per environment","Confirm SUPABASE_URL points at the intended database","Check the channel's scope is 'global'"],"tags":["database","postgrest","lookup"],"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"}