{"record":{"id":"294564a0ca313148","repo":"overleaf/overleaf","slug":"invalid-buildid-294564","errorCode":null,"errorMessage":"invalid ?buildId","messagePattern":"invalid \\?buildId","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"services/web/app/src/Features/Compile/CompileController.mjs","lineNumber":90,"sourceCode":"    res,\n    'compile-with-checkpoint',\n    { includeReferer: true }\n  )\n\n  return {\n    compileFromHistory,\n    pdfDownloadDomain,\n    enablePdfCaching,\n    pdfCachingMinChunkSize,\n    enableCheckpoint,\n  }\n}\n\nasync function _syncTeX(req, res, direction, validatedOptions) {\n  const projectId = req.params.Project_id\n  const { editorId, buildId, clsiserverid: clsiServerId } = req.query\n  if (!editorId?.match(/^[a-f0-9-]+$/)) throw new Error('invalid ?editorId')\n  if (!buildId?.match(/^[a-f0-9-]+$/)) throw new Error('invalid ?buildId')\n\n  const userId = CompileController._getUserIdForCompile(req)\n  try {\n    const body = await CompileManager.promises.syncTeX(projectId, userId, {\n      direction,\n      compileFromClsiCache: Features.hasFeature('saas'),\n      validatedOptions: {\n        ...validatedOptions,\n        editorId,\n        buildId,\n      },\n      clsiServerId,\n    })\n    res.json(body)\n  } catch (err) {\n    if (err instanceof Errors.NotFoundError) return res.status(404).end()\n    throw err\n  }","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/web/app/src/Features/Compile/CompileController.mjs#L72-L108","documentation":"_syncTeX validates ?buildId against /^[a-f0-9-]+$/ before calling CompileManager.promises.syncTeX. A missing, empty, or malformed buildId throws Error('invalid ?buildId'). The regex requires lowercase hex digits and hyphens only.","triggerScenarios":"Calling the synctex proxy endpoints with ?buildId absent, empty, uppercase, or containing illegal characters (e.g. buildId=123, buildId=null, buildId with URL-encoded slashes).","commonSituations":"Frontend sends a buildId captured before the first successful compile (undefined); stale client code still using the old requestType/token params; buildId stored with uppercase letters from a different ID format; deep links to synctex with the param stripped.","solutions":["Only issue synctex requests after a compile has returned a buildId; guard the client call.","Normalize the buildId to the server's format (lowercase hex with hyphens) before sending.","If building deep links, include the current buildId in the URL.","Catch this error client-side and trigger a recompile to obtain a valid buildId."],"exampleFix":"// before\nfetch(`/project/${projectId}/sync/pdf?buildId=${buildId}&page=${page}`)\n// after\nif (!/^[a-f0-9-]+$/.test(buildId ?? '')) {\n  await recompile() // get a fresh valid buildId\n}\nfetch(`/project/${projectId}/sync/pdf?buildId=${buildId}&page=${page}`)","handlingStrategy":"validation","validationCode":"const BUILD_ID_RE = /^[a-f0-9-]+$/\nif (!BUILD_ID_RE.test(buildId ?? '')) { await recompile(); /* use new buildId */ }","typeGuard":"function isBuildId(v) { return typeof v === 'string' && /^[a-f0-9-]+$/.test(v) }","tryCatchPattern":"try {\n  await syncTeX({ buildId })\n} catch (err) {\n  if (err.message === 'invalid ?buildId') {\n    await recompileAndRetry()\n  } else throw err\n}","preventionTips":["Gate synctex calls behind a 'compile completed' flag holding a valid buildId.","Never persist/restore stale buildIds across sessions without revalidation.","Mirror the server regex in client-side validation."],"tags":["validation","query-params","synctex","input-validation"],"backgroundTag":"invalid-query-parameter","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}