affaan-m/ECC · error · Error
invalid repository
Error message
invalid repository
What it means
announcementKey composes a stable 'owner/repo:discussion:<id>' key from a repository string and discussion id. It validates the repository against /^[^/\s]+\/[^/\s]+$/ (exactly one slash, no whitespace) and throws 'invalid repository' when the value is missing, malformed, or contains spaces. This prevents downstream keys and footer references from being ambiguous.
Solutions
- Pass the repository as 'owner/repo' (e.g. 'affaan-m/ECC'), sourced from the GITHUB_REPOSITORY env var or an explicit config field.
- If you have a URL, normalize it first: new URL(u).pathname.replace(/^\//,'').replace(/\.git$/,'').
- Log or validate the config object before calling announcementKey so an undefined repository fails fast with a clear message.
- Trim whitespace on the input before constructing the object.
Example fix
// before
const key = announcementKey({ repository: 'https://github.com/affaan-m/ECC', discussionId });
// after
const repo = (process.env.GITHUB_REPOSITORY || 'affaan-m/ECC').trim();
const key = announcementKey({ repository: repo, discussionId }); Defensive patterns
Strategy: validation
Validate before calling
const isValidRepo = (repo) => typeof repo === 'string' && /^[^\/\s]+\/[^\/\s]+$/.test(repo.trim());
if (!isValidRepo(process.env.GITHUB_REPOSITORY)) throw new Error('Expected repository as owner/repo'); Type guard
const isOwnerRepo = (v) => typeof v === 'string' && /^[^\/\s]+\/[^\/\s]+$/.test(v);
Try / catch
try {
const key = announcementKey({ repository, discussionId });
} catch (err) {
if (err.message === 'invalid repository') {
console.error(`repository must be 'owner/repo', got: ${JSON.stringify(repository)}`);
}
throw err;
} Prevention
- Source the repository from GITHUB_REPOSITORY rather than parsing URLs by hand.
- Normalize any URL to owner/repo before calling announcementKey.
- Trim inputs and reject undefined early with a descriptive error.
- Unit-test announcementKey with URL-shaped and empty inputs.
When it happens
Trigger: main() passes an input object whose repository field is undefined/null, an empty string, a bare repo name like 'ECC' without owner, a full URL like 'https://github.com/owner/repo', or a value containing whitespace or multiple slashes.
Common situations: Parsing the repo from GITHUB_REPOSITORY incorrectly; passing a GitHub URL instead of 'owner/repo'; forgetting to thread the repository through a config object so it arrives undefined.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- invalid discussion id
- invalid cron expression
- Invalid format: . Use text or json.
- invalid issueNumber: expected positive integer, got
- invalid repo: expected non-empty string, got
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/c22034f3008d23ea.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/discord/announcement-core.mjs:24
return discussion?.category?.name === 'Announcements';
}
export function releaseMarker(tag) {
const normalized = String(tag || '').trim();
if (!normalized) throw new Error('release tag is required');
return `<!-- ecc-release:${normalized} -->`;
}
export function findReleaseDiscussion(discussions, marker) {
return discussions.find(item => (
item?.category?.name === 'Announcements'
&& typeof item.body === 'string'
&& item.body.includes(marker)
)) || null;
}
export function announcementKey({ repository, discussionId }) {
if (!/^[^/\s]+\/[^/\s]+$/.test(String(repository || ''))) throw new Error('invalid repository');
if (!/^[A-Za-z0-9_-]+$/.test(String(discussionId || ''))) throw new Error('invalid discussion id');
return `${repository}:discussion:${discussionId}`;
}
export function buildDiscordPayload({ title, body, url, key }) {
const discussionId = String(key).split(':').at(-1);
const footer = `ecc:${discussionId}`;
const description = String(body || '').trim().slice(0, DISCORD_DESCRIPTION_LIMIT);
const nonce = `ecc-${createHash('sha256').update(String(key)).digest('hex').slice(0, 16)}`;
return {
allowed_mentions: { parse: [] },
nonce,
enforce_nonce: true,
embeds: [{
title: String(title || 'ECC announcement').trim().slice(0, 256),
description,
url: String(url || ''),
footer: { text: footer },View on GitHub (pinned to 8321021c54)