affaan-m/ECC · error
invalid repo: expected non-empty string, got
Error message
invalid repo: expected non-empty string, got ${JSON.stringify(repo)} What it means
assertValidRepo is the entry-point guard used by every apply* action (claim, sync, validate, publish, review, decompose) in the github-coordination library. It requires the repo argument to be a non-empty, non-whitespace string identifying the GitHub repository. Failing that, it throws with a JSON dump of the received value so the caller can see what was actually passed.
Solutions
- Pass a repo string like 'owner/name' (or resolve it from process.env.GITHUB_REPOSITORY) to the apply* function
- Fix the config file or CLI flag so the repo value is actually populated
- Validate/normalize the repo (e.g. via normalizeRepo) before calling the action
- Check that CI provides GITHUB_REPOSITORY and that your script reads it correctly
Example fix
// before
applyPublish(options.repo, 42, {}) // options.repo undefined
// after
const repo = options.repo || process.env.GITHUB_REPOSITORY;
if (typeof repo !== 'string' || !repo.trim()) throw new Error('repo not configured');
applyPublish(repo, 42, {}) Defensive patterns
Strategy: type-guard
Validate before calling
function assertRepo(repo) { if (typeof repo !== 'string' || !repo.trim()) throw new Error(`repo not configured: ${JSON.stringify(repo)}`); }
assertRepo(repo); Type guard
const isValidRepoArg = (repo) => typeof repo === 'string' && repo.trim().length > 0;
Try / catch
try { applyClaim(repo, n, opts); } catch (e) { if (String(e.message).startsWith('invalid repo')) console.error('resolve repo from config or GITHUB_REPOSITORY before calling'); throw e; } Prevention
- Default to process.env.GITHUB_REPOSITORY when the repo option is absent
- Validate repo at the CLI/config entry point once, before any apply* call
- Avoid passing parsed objects where the 'owner/name' string is expected
- Fail fast at startup if the repo configuration is missing
When it happens
Trigger: Calling applyClaim/applySync/applyValidate/applyPublish/applyReview/applyDecompose with repo = undefined/null (missing config field), an empty or whitespace-only string, or a non-string value such as a number or object.
Common situations: Missing GITHUB_REPOSITORY env var in CI so the repo option resolves to undefined; a config file where the repo key is empty or commented out; passing a parsed object like {owner, repo} instead of the 'owner/name' string; shell interpolation expanding to an empty value.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- invalid issueNumber: expected positive integer, got
- fps must be positive, got
- image_to_3d needs at least one image
- image_to_3d needs at least one image
- Invalid Claude plugin root: expected a string
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/985d92153bbabb43.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/github-coordination/actions.js:21
const { loadPolicy } = require('./policy');
const { mergeIssueBody, normalizeBodyForComparison } = require('./parsing');
const { getIssue, listIssues, editIssue, commentIssue, normalizeLabels } = require('./gh-api');
const {
assertIssueClaimable,
buildIssueComment,
buildIssueStateFromAction,
desiredLabelsForState,
getCoordinationState,
summarizeStateForOutput,
syncIssueLabels,
verifyDependenciesClosed,
} = require('./state');
const { upsertCoordinationWorkItem } = require('./store');
const { extractIssueReferences, extractTasks } = require('./parsing');
function assertValidRepo(repo) {
if (typeof repo !== 'string' || !repo.trim()) {
throw new Error(`invalid repo: expected non-empty string, got ${JSON.stringify(repo)}`);
}
}
function assertValidIssueNumber(issueNumber) {
if (!Number.isFinite(issueNumber) || issueNumber <= 0 || !Number.isInteger(issueNumber)) {
throw new Error(`invalid issueNumber: expected positive integer, got ${JSON.stringify(issueNumber)}`);
}
}
function staleCoordinationLabels(issue, nextLabels, policy) {
const epicLabel = policy.labels && policy.labels.epic;
return normalizeLabels(issue.labels).filter(l =>
(l.startsWith('coordination:') || l === epicLabel) && !nextLabels.includes(l)
);
}
// applyClaim performs a read (getIssue) → check (assertIssueClaimable) → write
// (editIssue) sequence that is NOT atomic. Two concurrent callers can both readView on GitHub (pinned to 8321021c54)