{"record":{"id":"d44150d472223bd2","repo":"mozilla/pdf.js","slug":"invalid-factory-url-val-must-include-trailin","errorCode":null,"errorMessage":"Invalid factory url: \"${val}\" must include trailing slash.","messagePattern":"Invalid factory url: \"(.+?)\" must include trailing slash\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/display/api_utils.js","lineNumber":98,"sourceCode":"    ArrayBuffer.isView(val) ||\n    (typeof val === \"object\" && !isNaN(val?.length))\n  ) {\n    return new Uint8Array(val);\n  }\n  throw new Error(\n    \"Invalid PDF binary data: either TypedArray, \" +\n      \"string, or array-like object is expected in the data property.\"\n  );\n}\n\nfunction getFactoryUrlProp(val) {\n  if (typeof val !== \"string\") {\n    return null;\n  }\n  if (val.endsWith(\"/\")) {\n    return val;\n  }\n  throw new Error(`Invalid factory url: \"${val}\" must include trailing slash.`);\n}\n\nconst isRefProxy = v =>\n  typeof v === \"object\" &&\n  Number.isInteger(v?.num) &&\n  v.num >= 0 &&\n  Number.isInteger(v?.gen) &&\n  v.gen >= 0;\n\nconst isNameProxy = v => typeof v === \"object\" && typeof v?.name === \"string\";\n\nconst isValidExplicitDest = _isValidExplicitDest.bind(\n  null,\n  /* validRef = */ isRefProxy,\n  /* validName = */ isNameProxy\n);\n\nclass LoopbackPort {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/display/api_utils.js#L80-L116","documentation":"Thrown by getFactoryUrlProp when a configured cMapUrl, standardFontDataUrl, or wasmUrl is a string that does not end with '/'. PDF.js builds resource URLs via simple concatenation (baseUrl + filename), so a missing trailing slash produces paths like 'https://x/cmaps' + 'JP' rather than 'https://x/cmaps/JP', silently breaking CJK/font/WASM fetching. The check makes this a hard error instead.","triggerScenarios":"Passing getDocument({ cMapUrl: 'https://cdn.example/cmaps' }), or any of the three factory URLs without the closing slash.","commonSituations":"Copy-pasting a base URL from a hosting panel that strips trailing slashes; switching from a version of pdfjs that auto-appended '/' to one that does not; CDN config that canonicalizes URLs without slashes.","solutions":["Append '/' to the configured URL: cMapUrl: 'https://cdn.example.com/cmaps/'.","Normalize at config time: const withSlash = url.endsWith('/') ? url : url + '/'.","Verify the same for standardFontDataUrl and wasmUrl when used."],"exampleFix":"// before\ngetDocument({ data, cMapUrl: 'https://cdn.example.com/cmaps' });\n\n// after\ngetDocument({ data, cMapUrl: 'https://cdn.example.com/cmaps/' });","handlingStrategy":"validation","validationCode":"function ensureTrailingSlash(u) {\n  return typeof u === 'string' && !u.endsWith('/') ? u + '/' : u;\n}\ngetDocument({\n  data,\n  cMapUrl: ensureTrailingSlash(config.cMapUrl),\n  standardFontDataUrl: ensureTrailingSlash(config.standardFontDataUrl),\n});","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Normalize every factory URL with a trailing slash at config build time.","Keep config in one place so the rule is applied uniformly.","Add a CI check that asserts these URLs end with '/'."],"tags":["config","cmaps","fonts","url"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}