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

  1. Pass a repo string like 'owner/name' (or resolve it from process.env.GITHUB_REPOSITORY) to the apply* function
  2. Fix the config file or CLI flag so the repo value is actually populated
  3. Validate/normalize the repo (e.g. via normalizeRepo) before calling the action
  4. 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

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


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 read

View on GitHub (pinned to 8321021c54)