bmad-code-org/BMAD-METHOD · error · Error

resolveChannel: unknown channel '${channel}'

Error message

resolveChannel: unknown channel '${channel}'

What it means

Thrown by resolveChannel when the channel argument is none of 'stable', 'next', or 'pinned'. This is a programmer/contract error: the channel resolver only knows those three strategies.

Source

Thrown at tools/installer/modules/channel-resolver.js:175

      // No GitHub URL — caller must handle by falling back to next.
      return { channel: 'next', ref: null, version: 'main', resolvedFallback: true, reason: 'not-a-github-url' };
    }

    try {
      const tags = await fetchStableTags(parsed.owner, parsed.repo, { timeout });
      if (tags.length === 0) {
        return { channel: 'next', ref: null, version: 'main', resolvedFallback: true, reason: 'no-stable-tags' };
      }
      const top = tags[0];
      return { channel: 'stable', ref: top.tag, version: top.tag, resolvedFallback: false };
    } catch (error) {
      // Propagate the error; callers decide whether to fall back or abort.
      error.message = `Failed to resolve stable channel for ${parsed.owner}/${parsed.repo}: ${error.message}`;
      throw error;
    }
  }

  throw new Error(`resolveChannel: unknown channel '${channel}'`);
}

/**
 * Verify that a specific tag exists in a GitHub repo. Used to validate
 * --pin values before the user sits through a long clone that then fails.
 */
async function tagExists(owner, repo, tagName, { timeout } = {}) {
  const url = `${GITHUB_API_BASE}/repos/${owner}/${repo}/git/refs/tags/${encodeURIComponent(tagName)}`;
  try {
    await fetchJson(url, { timeout });
    return true;
  } catch (error) {
    if (error.statusCode === 404) return false;
    throw error;
  }
}

/**

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Use one of the supported channels: 'stable', 'next', or 'pinned'.
  2. Inspect the channel-plan/channel-resolver output to find where the bad channel string originates.
  3. If migrating from an older vocabulary, normalize legacy channel names before calling resolveChannel.

Example fix

// before
await resolveChannel({ channel: 'latest', repoUrl });

// after
await resolveChannel({ channel: 'stable', repoUrl });
Defensive patterns

Strategy: type-guard

Validate before calling

const SUPPORTED = new Set(['stable', 'next', 'pinned']);
if (!SUPPORTED.has(channel)) {
  throw new Error(`Unsupported channel '${channel}'. Use stable, next, or pinned.`);
}

Type guard

function isSupportedChannel(channel) {
  return channel === 'stable' || channel === 'next' || channel === 'pinned';
}

Try / catch

try {
  await resolveChannel({ channel, repoUrl });
} catch (error) {
  if (error.message.startsWith("resolveChannel: unknown channel '")) { /* normalize channel */ }
  throw error;
}

Prevention

When it happens

Trigger: resolveChannel({ channel: <something else>, ... }) — e.g. channel='latest', undefined, null, or a typo like 'stab le'.

Common situations: A module config or channel-plan builder emitted an unsupported channel string, a typo in CLI input, or an unhandled migration from an old channel vocabulary.

Related errors


AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13). Data as JSON: /api/errors/08207992a1aa7f4f. Report an issue: GitHub.