{"record":{"id":"3c07acd980d3ad89","repo":"amruthpillai/reactive-resume","slug":"bad-request-3c07ac","errorCode":"BAD_REQUEST","errorMessage":"Invalid public PDF fallback metadata.","messagePattern":"Invalid public PDF fallback metadata\\.","errorType":"exception","errorClass":"ORPCError","httpStatus":400,"severity":"error","filePath":"packages/api/src/features/resume/public-pdf.ts","lineNumber":71,"sourceCode":"\trateLimiter: publicRenderRateLimiter,\n\trenderPdf: async (input) => (await import(\"@reactive-resume/pdf/server\")).createResumePdfFile(input),\n\tgetFingerprints: async () =>\n\t\t(await import(\"@reactive-resume/pdf/public-projection\")).getPublicStyleProjectionFingerprints(),\n\tnow: Date.now,\n\tobserve: console.info,\n};\n\nexport async function createPublicResumePdf(\n\tinput: CreatePublicResumePdfInput,\n\tdependencies: PublicResumePdfDependencies = defaultDependencies,\n): Promise<{ body: File; filename: string }> {\n\tconst fingerprintPattern = /^[a-f0-9]{64}$/;\n\tif (\n\t\t!PUBLIC_RESUME_PDF_MISMATCH_REASONS.includes(input.mismatchReason) ||\n\t\t(input.clientRegistryFingerprint !== undefined && !fingerprintPattern.test(input.clientRegistryFingerprint)) ||\n\t\t(input.clientAdapterFingerprint !== undefined && !fingerprintPattern.test(input.clientAdapterFingerprint))\n\t) {\n\t\tthrow new ORPCError(\"BAD_REQUEST\", { status: 400, message: \"Invalid public PDF fallback metadata.\" });\n\t}\n\tconst currentUserId = await dependencies.resolveCurrentUserId(input.requestHeaders);\n\tconst resume = await loadAuthorizedPublicRenderResume(\n\t\t{\n\t\t\tusername: input.username,\n\t\t\tslug: input.slug,\n\t\t\trequestHeaders: input.requestHeaders,\n\t\t\ttrustedClient: input.trustedClient,\n\t\t\t...(currentUserId ? { currentUserId } : {}),\n\t\t},\n\t\tdependencies,\n\t);\n\tdependencies.rateLimiter.consume({ trustedClient: input.trustedClient, resumeId: resume.id });\n\tconst startedAt = dependencies.now();\n\tconst fingerprints = await dependencies.getFingerprints();\n\tconst event = (success: boolean) => {\n\t\tdependencies.observe({\n\t\t\tname: \"semantic_css.render_fallback\",","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/amruthpillai/reactive-resume/blob/3a5b12e2a40374a9571988701fcb75c5a1831c42/packages/api/src/features/resume/public-pdf.ts#L53-L89","documentation":"createPublicResumePdf() validates the semantic-CSS fallback metadata before doing any work: mismatchReason must be one of the eight PUBLIC_RESUME_PDF_MISMATCH_REASONS, and any provided clientRegistryFingerprint/clientAdapterFingerprint must match /^[a-f0-9]{64}$/ (lowercase SHA-256 hex). This is the trust boundary for the public fallback render path.","triggerScenarios":"Calling the public resume PDF fallback with an unknown mismatchReason, a fingerprint of the wrong length, or non-hex characters (uppercase, a 'sha256:' prefix, base64, etc.).","commonSituations":"A client using an outdated reason vocabulary after a schema bump, a fingerprint computed with a different hash algorithm or encoding, or a malformed probe request.","solutions":["Send only mismatchReason values from PUBLIC_RESUME_PDF_MISMATCH_REASONS.","Compute fingerprints as lowercase hex SHA-256 (exactly 64 chars).","Omit fingerprints when unknown rather than sending malformed values."],"exampleFix":"// before\ncreatePublicResumePdf({ ..., mismatchReason: 'unknown', clientRegistryFingerprint: 'ABC123' });\n// after\nconst REASONS = ['missing-projection','format-version','language-version','semantic-tree-version','registry-fingerprint','adapter-fingerprint','render-data-hash','invalid-projection'] as const;\nconst fp = crypto.createHash('sha256').update(payload).digest('hex'); // 64 lowercase hex\ncreatePublicResumePdf({ ..., mismatchReason: REASONS[i], clientRegistryFingerprint: fp });","handlingStrategy":"validation","validationCode":"const REASONS = ['missing-projection','format-version','language-version','semantic-tree-version','registry-fingerprint','adapter-fingerprint','render-data-hash','invalid-projection'] as const;\nconst FP = /^[a-f0-9]{64}$/;\nfunction validateFallbackMeta(reason, registryFp, adapterFp) {\n  if (!REASONS.includes(reason)) throw new Error(`Bad mismatchReason: ${reason}`);\n  if (registryFp !== undefined && !FP.test(registryFp)) throw new Error('Bad registry fingerprint');\n  if (adapterFp !== undefined && !FP.test(adapterFp)) throw new Error('Bad adapter fingerprint');\n}","typeGuard":"function isMismatchReason(value: unknown): value is PublicResumePdfMismatchReason {\n  return typeof value === 'string' && PUBLIC_RESUME_PDF_MISMATCH_REASONS.includes(value as any);\n}","tryCatchPattern":null,"preventionTips":["Keep the client's mismatch-reason vocabulary in sync with PUBLIC_RESUME_PDF_MISMATCH_REASONS.","Compute fingerprints as lowercase hex SHA-256 (64 chars), never base64 or uppercase.","Omit fingerprints when unsure rather than sending malformed values."],"tags":["resume","public-pdf","validation","semantic-css","bad-request"],"backgroundTag":null,"analyzedSha":"3a5b12e2a40374a9571988701fcb75c5a1831c42","analyzedAt":"2026-08-12T22:31:22.666Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}