{"record":{"id":"990fb95a3a91f6e7","repo":"affaan-m/ECC","slug":"release-tag-is-required","errorCode":null,"errorMessage":"release tag is required","messagePattern":"release tag is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/discord/announcement-core.mjs","lineNumber":11,"sourceCode":"import { createHash } from 'node:crypto';\n\nconst DISCORD_DESCRIPTION_LIMIT = 4000;\n\nexport function isAnnouncementDiscussion(discussion) {\n  return discussion?.category?.name === 'Announcements';\n}\n\nexport function releaseMarker(tag) {\n  const normalized = String(tag || '').trim();\n  if (!normalized) throw new Error('release tag is required');\n  return `<!-- ecc-release:${normalized} -->`;\n}\n\nexport function findReleaseDiscussion(discussions, marker) {\n  return discussions.find(item => (\n    item?.category?.name === 'Announcements'\n    && typeof item.body === 'string'\n    && item.body.includes(marker)\n  )) || null;\n}\n\nexport function announcementKey({ repository, discussionId }) {\n  if (!/^[^/\\s]+\\/[^/\\s]+$/.test(String(repository || ''))) throw new Error('invalid repository');\n  if (!/^[A-Za-z0-9_-]+$/.test(String(discussionId || ''))) throw new Error('invalid discussion id');\n  return `${repository}:discussion:${discussionId}`;\n}\n\nexport function buildDiscordPayload({ title, body, url, key }) {","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/discord/announcement-core.mjs#L1-L29","documentation":"releaseMarker builds the HTML comment marker used to locate or create the release announcement discussion. It requires a non-empty tag string; when called with undefined, null, or an empty/whitespace-only string it throws 'release tag is required'. This guards downstream markers like <!-- ecc-release:v1.2.3 --> which would otherwise be ambiguous and match nothing.","triggerScenarios":"main() or marker calls releaseMarker(tag) with the release tag input from CI/env (e.g. GITHUB_REF_NAME) when no tag is present: running the announcement script outside a tag push, or with RELEASE_TAG unset or whitespace-only.","commonSituations":"Workflow triggered manually (workflow_dispatch) with no tag input; git tag not yet created; environment variable name mismatch between CI and script; trailing-whitespace-only env value.","solutions":["Invoke the script only for tag push events or pass the tag explicitly: node scripts/discord/release-announce.mjs v1.2.3.","In CI, verify the env var that feeds the tag is populated (e.g. ${GITHUB_REF_NAME}) and pass it to the script.","Add an early guard in the workflow so the job skips (or fails clearly) when the tag is empty.","Strip the refs/tags/ prefix and trim whitespace before calling releaseMarker."],"exampleFix":"// before\nconst marker = releaseMarker(process.env.RELEASE_TAG);\n// after\nconst rawTag = (process.env.RELEASE_TAG || process.argv[2] || '').replace(/^refs\\/tags\\//, '').trim();\nif (!rawTag) { console.error('No release tag provided'); process.exit(1); }\nconst marker = releaseMarker(rawTag);","handlingStrategy":"validation","validationCode":"function hasReleaseTag(tag) {\n  return typeof tag === 'string' && tag.trim().length > 0;\n}\nif (!hasReleaseTag(process.env.RELEASE_TAG)) process.exit(1);","typeGuard":"const isNonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":"try {\n  const marker = releaseMarker(tag);\n} catch (err) {\n  if (err.message === 'release tag is required') {\n    console.error('Run this job only on tag push, or pass the tag explicitly.');\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Trigger announcement workflows only on pushes of tags matching a pattern (on: push: tags: ['v*']).","Normalize GITHUB_REF_NAME by stripping the refs/tags/ prefix before use.","Add an early job step asserting the tag env var is non-empty.","Avoid whitespace-only tag values when accepting workflow_dispatch inputs."],"tags":["validation","release","ci"],"backgroundTag":"empty-required-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}