Hmbown/CodeWhale · error · Error
channel does not exist
Error message
channel ${channel} does not exist What it means
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.
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
Example fix
// before node web/scripts/facts-publish.mjs revoke --channel stabel --version 3 --reason x // after node web/scripts/facts-publish.mjs revoke --channel stable --version 3 --reason x
Defensive patterns
Strategy: validation
Validate before calling
const res = await fetch(`${SUPABASE_URL}/rest/v1/facts_channel?scope=eq.global&slug=eq.${encodeURIComponent(channel)}&select=id`, { headers: { apikey: KEY } });
const rows = await res.json();
if (!rows?.length) throw new Error(`channel ${channel} does not exist before revoke`); Try / catch
try {
await publish("revoke", ["--channel", channel, "--version", String(v), "--reason", reason]);
} catch (e) {
if (/does not exist/.test(e.message)) {
console.error(`Channel '${channel}' not found in global scope; check slug/environment`);
}
} Prevention
- 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'
When it happens
Trigger: `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.
Common situations: 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.
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
- Agent session not found
- Ambiguous prefix ' ' matches sessions
- Ambiguous task prefix
- Another pet recorder is using this output.
- channel does not exist
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/146d22f30bee9532.
Report an issue: GitHub.
Appendix: source
Thrown at web/scripts/facts-publish.mjs:611
}
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",
body: { status: "revoked", revoked_at: nowIso(), revoke_reason: reason },
prefer: "return=representation",
});
console.log(JSON.stringify({ revoked: updated?.length ?? 0, channel, facts_version: version }, null, 2));
return 0;
}
throw new Error(`unknown command ${cmd}`);
}
const invokedDirectly = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
if (invokedDirectly) {
main(process.argv.slice(2)).then((code) => process.exit(code)).catch((err) => {
console.error(`facts-publish: ${err.message}`);
process.exit(1);
});
}View on GitHub (pinned to 433685b202)