{"record":{"id":"0b65318b648ae68b","repo":"overleaf/overleaf","slug":"invalid-v-parameter","errorCode":null,"errorMessage":"invalid v parameter","messagePattern":"invalid v parameter","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"services/web/app/src/Features/Compile/CompileController.mjs","lineNumber":543,"sourceCode":"      buildId,\n      file,\n      clsiServerId,\n      'output-file',\n      req,\n      res\n    )\n  },\n\n  async proxySyncPdf(req, res) {\n    const { page, h, v } = req.query\n    if (!page?.match(/^\\d+$/)) {\n      throw new Error('invalid page parameter')\n    }\n    if (!h?.match(/^-?\\d+\\.\\d+$/)) {\n      throw new Error('invalid h parameter')\n    }\n    if (!v?.match(/^-?\\d+\\.\\d+$/)) {\n      throw new Error('invalid v parameter')\n    }\n    await _syncTeX(req, res, 'pdf', { page, h, v })\n  },\n\n  async proxySyncCode(req, res) {\n    const { file, line, column } = req.query\n    if (file == null) {\n      throw new Error('missing file parameter')\n    }\n    // Check that we are dealing with a simple file path (this is not\n    // strictly needed because synctex uses this parameter as a label\n    // to look up in the synctex output, and does not open the file\n    // itself).  Since we have valid synctex paths like foo/./bar we\n    // allow those by replacing /./ with /\n    const testPath = file.replace('/./', '/')\n    if (Path.resolve('/', testPath) !== `/${testPath}`) {\n      throw new Error('invalid file parameter')\n    }","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/web/app/src/Features/Compile/CompileController.mjs#L525-L561","documentation":"proxySyncPdf validates the ?v query parameter against /^-?\\d+\\.\\d+$/, the same signed-decimal format as h. Missing, integer-only, comma-decimal, or non-numeric v values throw Error('invalid v parameter').","triggerScenarios":"GET /project/:Project_id/sync/pdf with v absent, v=0 (no decimal), v=10.5.3 (two dots), v=-10 (no fraction), or non-numeric text.","commonSituations":"Vertical PDF position serialized from an integer coordinate; v omitted because the caller considered it optional; locale comma decimals; NaN stringified as 'NaN' from an uninitialized position variable.","solutions":["Serialize v with toFixed(2) so it always matches the signed-decimal format.","Include v in the query even for zero: v=0.00.","Fix the position-computation code that yields NaN or integers before the request.","Convert any comma decimal separator to a dot client-side."],"exampleFix":"// before\nconst params = { page, h, v } // v could be 7\nfetch(`/project/${id}/sync/pdf?${new URLSearchParams(params)}`)\n// after\nconst params = { page, h: Number(h).toFixed(2), v: Number(v).toFixed(2) }\nfetch(`/project/${id}/sync/pdf?${new URLSearchParams(params)}`)","handlingStrategy":"validation","validationCode":"function validCoord(v) { return /^-?\\d+\\.\\d+$/.test(String(v)) }\n// serialize: v.toFixed(2)","typeGuard":"function isSignedDecimal(v) { return typeof v === 'string' && /^-?\\d+\\.\\d+$/.test(v) }","tryCatchPattern":"try {\n  await syncPdf({ v })\n} catch (err) {\n  if (err.message === 'invalid v parameter') {\n    // fix serialization (toFixed) and retry\n  } else throw err\n}","preventionTips":["Format both h and v with toFixed(2) together.","Guard against NaN coordinates before building the URL.","Include v even when zero: v=0.00."],"tags":["validation","query-params","synctex","number-format"],"backgroundTag":"invalid-query-parameter","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}