{"record":{"id":"ee8e6c1cf2ed1d7b","repo":"discordjs/discord.js","slug":"the-tag-option-can-only-be-used-with-dev","errorCode":null,"errorMessage":"The --tag option can only be used with --dev","messagePattern":"The --tag option can only be used with --dev","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/actions/src/releasePackages/index.ts","lineNumber":49,"sourceCode":"\t\t'-e, --exclude <packages...>',\n\t\t'exclude specific packages from releasing (will still release if necessary for another package)',\n\t\texcludeInput ? excludeInput.split(',') : [],\n\t)\n\t.option('--dry', 'skips actual publishing and outputs logs instead', dryInput)\n\t.option('--dev', 'publishes development versions and skips tagging / github releases', devInput)\n\t.option('--tag <tag>', 'tag to use for dev releases (defaults to \"dev\")', getInput('tag'))\n\t.parse();\n\nconst {\n\texclude,\n\tdry,\n\tdev,\n\ttag: inputTag,\n} = program.opts<{ dev: boolean; dry: boolean; exclude: string[]; tag: string }>();\n\n// All this because getInput('tag') will return empty string when not set :P\nif (!dev && inputTag.length) {\n\tthrow new Error('The --tag option can only be used with --dev');\n}\n\nconst tag = inputTag.length ? inputTag : dev ? 'dev' : undefined;\nconst [packageName] = program.processedArgs as [string];\nconst tree = await generateReleaseTree(dry, tag, packageName, exclude);\n\ninterface ReleaseResult {\n\tidentifier: string;\n\turl: string;\n}\n\nconst publishedPackages: ReleaseResult[] = [];\nconst skippedPackages: ReleaseResult[] = [];\n\nfor (const branch of tree) {\n\tstartGroup(`Releasing ${branch.map((entry) => `${entry.name}@${entry.version}`).join(', ')}`);\n\n\tawait Promise.all(","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/discordjs/discord.js/blob/a81ed8a306d37fdc746e26a634b6a42164ba2c8c/packages/actions/src/releasePackages/index.ts#L31-L67","documentation":"The release CLI parses --tag from command options. Tags are a dev-release concept (getInput('tag') returns an empty string when unset, so the code checks length). Passing --tag without --dev is invalid because a named tag has no meaning for non-dev releases, so the script throws immediately after option parsing.","triggerScenarios":"Invoking the release CLI with `--tag <value>` but without the `--dev` flag, i.e. `!dev && inputTag.length` is true.","commonSituations":"Copy-pasting a dev-release command and dropping --dev; CI templates that always inject a tag argument; scripting the release command with variables where tag is set but dev is false.","solutions":["Add the --dev flag when using --tag.","Remove the --tag option if you intend a normal (non-dev) release.","Fix the CI workflow/script so tag is only passed when dev mode is enabled."],"exampleFix":"// before\npnpm release all --tag nightly\n// after\npnpm release all --dev --tag nightly","handlingStrategy":"validation","validationCode":"if (!dev && inputTag.length) {\n  throw new Error('The --tag option can only be used with --dev');\n}","typeGuard":"const isDevTagOptions = (o: { dev: boolean; tag: string }) => !o.tag.length || o.dev;","tryCatchPattern":"try {\n  await runRelease(args);\n} catch (err) {\n  if ((err as Error).message.includes('--tag option can only be used with --dev')) {\n    console.error('Invalid flags: either drop --tag or add --dev.');\n    process.exitCode = 1;\n  } else throw err;\n}","preventionTips":["Build release commands from templates that pair --tag with --dev.","In CI, gate tag injection behind the dev-release condition.","Validate flag combinations before invoking the CLI."],"tags":["cli","arguments","configuration"],"backgroundTag":"invalid-cli-flag-combination","analyzedSha":"a81ed8a306d37fdc746e26a634b6a42164ba2c8c","analyzedAt":"2026-08-30T04:07:22.193Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}