{"record":{"id":"9bcba0f9589aa5d0","repo":"badges/shields","slug":"version-invalid","errorCode":null,"errorMessage":"version invalid","messagePattern":"version invalid","errorType":"exception","errorClass":"InvalidResponse","httpStatus":null,"severity":"error","filePath":"services/opm/opm-version.service.js","lineNumber":56,"sourceCode":"        method: 'HEAD',\n        searchParams: {\n          account: user,\n          name: moduleName,\n        },\n      },\n      httpErrors: {\n        404: 'module not found',\n      },\n    })\n\n    // TODO: set followRedirect to false and intercept 302 redirects\n    const location = res.redirectUrls[0].toString()\n    if (!location) {\n      throw new NotFound({ prettyMessage: 'module not found' })\n    }\n    const version = location.match(`${moduleName}-(.+).opm`)[1]\n    if (!version) {\n      throw new InvalidResponse({ prettyMessage: 'version invalid' })\n    }\n    return version\n  }\n\n  async handle({ user, moduleName }) {\n    const version = await this.fetch({ user, moduleName })\n\n    return renderVersionBadge({ version })\n  }\n}\n","sourceCodeStart":38,"sourceCodeEnd":67,"githubUrl":"https://github.com/badges/shields/blob/766fd8bc89a90b8534dc573ab72dec30215ab1ec/services/opm/opm-version.service.js#L38-L67","documentation":"After obtaining the OPM redirect location, the service extracts the version via the regex `${moduleName}-(.+).opm`. If the match succeeds structurally but yields no capture group — or the URL does not follow the expected `<moduleName>-<version>.opm` naming — it throws InvalidResponse, meaning upstream returned something the service cannot parse into a version.","triggerScenarios":"The redirect Location URL exists but does not match `${moduleName}-(.+).opm` as expected (regex fails so `match(...)[1]` throws, or capture is empty), e.g. a changed filename scheme on the opm server.","commonSituations":"Upstream renaming of .opm artifacts, modules whose names embed the version differently, or a redirect to an HTML error page whose URL happens to be present but not a module file.","solutions":["Inspect the actual redirect Location header (curl -I) and compare it to the expected `<module>-<version>.opm` pattern.","Verify the moduleName in the badge URL exactly matches the published artifact filename prefix.","If the upstream filename scheme changed, the service regex needs updating — report/patch the service."],"exampleFix":"// before\nconst version = location.match(`${moduleName}-(.+).opm`)[1]\n// after\nconst m = location.match(`${moduleName}-(.+?)\\.opm`)\nif (!m) throw new InvalidResponse({ prettyMessage: 'version invalid' })\nconst version = m[1]","handlingStrategy":"try-catch","validationCode":"// Pre-check the redirect target matches the expected artifact naming\nconst loc = res.redirectUrls[0] && res.redirectUrls[0].toString()\nif (!loc || !new RegExp(`${moduleName}-(.+)\\\\.opm`).test(loc)) {\n  throw new Error('OPM redirect URL does not contain a parseable version')\n}","typeGuard":"function hasParseableVersion(location, moduleName) {\n  const m = location && location.match(`${moduleName}-(.+).opm`)\n  return Boolean(m && m[1])\n}","tryCatchPattern":"try {\n  const version = await getOpmVersion(user, moduleName)\n} catch (e) {\n  if (e.message === 'version invalid') {\n    // treat as upstream format change: fall back to 'unknown' badge\n  } else { throw e }\n}","preventionTips":["Inspect the actual Location header with curl -I when the badge breaks","Watch for upstream artifact-naming changes on opm.openresty.org","Guard regex extractions with null checks instead of indexing match()[1] directly"],"tags":["parsing","regex","invalid-response","opm","third-party-api"],"backgroundTag":"unexpected-response-format","analyzedSha":"766fd8bc89a90b8534dc573ab72dec30215ab1ec","analyzedAt":"2026-08-30T01:40:27.499Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}