{"record":{"id":"08207992a1aa7f4f","repo":"bmad-code-org/BMAD-METHOD","slug":"resolvechannel-unknown-channel-channel","errorCode":null,"errorMessage":"resolveChannel: unknown channel '${channel}'","messagePattern":"resolveChannel: unknown channel '(.+?)'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"tools/installer/modules/channel-resolver.js","lineNumber":175,"sourceCode":"      // No GitHub URL — caller must handle by falling back to next.\n      return { channel: 'next', ref: null, version: 'main', resolvedFallback: true, reason: 'not-a-github-url' };\n    }\n\n    try {\n      const tags = await fetchStableTags(parsed.owner, parsed.repo, { timeout });\n      if (tags.length === 0) {\n        return { channel: 'next', ref: null, version: 'main', resolvedFallback: true, reason: 'no-stable-tags' };\n      }\n      const top = tags[0];\n      return { channel: 'stable', ref: top.tag, version: top.tag, resolvedFallback: false };\n    } catch (error) {\n      // Propagate the error; callers decide whether to fall back or abort.\n      error.message = `Failed to resolve stable channel for ${parsed.owner}/${parsed.repo}: ${error.message}`;\n      throw error;\n    }\n  }\n\n  throw new Error(`resolveChannel: unknown channel '${channel}'`);\n}\n\n/**\n * Verify that a specific tag exists in a GitHub repo. Used to validate\n * --pin values before the user sits through a long clone that then fails.\n */\nasync function tagExists(owner, repo, tagName, { timeout } = {}) {\n  const url = `${GITHUB_API_BASE}/repos/${owner}/${repo}/git/refs/tags/${encodeURIComponent(tagName)}`;\n  try {\n    await fetchJson(url, { timeout });\n    return true;\n  } catch (error) {\n    if (error.statusCode === 404) return false;\n    throw error;\n  }\n}\n\n/**","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/bmad-code-org/BMAD-METHOD/blob/b70486b9bdcb0a404d329e2a763b57964e7f1360/tools/installer/modules/channel-resolver.js#L157-L193","documentation":"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.","triggerScenarios":"resolveChannel({ channel: <something else>, ... }) — e.g. channel='latest', undefined, null, or a typo like 'stab le'.","commonSituations":"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.","solutions":["Use one of the supported channels: 'stable', 'next', or 'pinned'.","Inspect the channel-plan/channel-resolver output to find where the bad channel string originates.","If migrating from an older vocabulary, normalize legacy channel names before calling resolveChannel."],"exampleFix":"// before\nawait resolveChannel({ channel: 'latest', repoUrl });\n\n// after\nawait resolveChannel({ channel: 'stable', repoUrl });","handlingStrategy":"type-guard","validationCode":"const SUPPORTED = new Set(['stable', 'next', 'pinned']);\nif (!SUPPORTED.has(channel)) {\n  throw new Error(`Unsupported channel '${channel}'. Use stable, next, or pinned.`);\n}","typeGuard":"function isSupportedChannel(channel) {\n  return channel === 'stable' || channel === 'next' || channel === 'pinned';\n}","tryCatchPattern":"try {\n  await resolveChannel({ channel, repoUrl });\n} catch (error) {\n  if (error.message.startsWith(\"resolveChannel: unknown channel '\")) { /* normalize channel */ }\n  throw error;\n}","preventionTips":["Restrict channel values to the supported set when building channel plans.","Add a normalization/migration step for any legacy channel vocabulary before resolving."],"tags":["channel-resolver","validation","modules"],"backgroundTag":null,"analyzedSha":"b70486b9bdcb0a404d329e2a763b57964e7f1360","analyzedAt":"2026-08-13T01:21:12.247Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}