{"record":{"id":"46bb0fcb907dae40","repo":"toeverything/AFFiNE","slug":"path-data-ended-short","errorCode":null,"errorMessage":"Path data ended short","messagePattern":"Path data ended short","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"blocksuite/affine/blocks/surface/src/utils/path-data-parser/parser.ts","lineNumber":119,"sourceCode":"          params[params.length] = +numbeToken.text;\n        } else {\n          throw new Error(\n            'Param not a number: ' + mode + ',' + numbeToken.text\n          );\n        }\n      }\n      if (typeof PARAMS[mode] === 'number') {\n        const segment: Segment = { key: mode, data: params };\n        segments.push(segment);\n        index += paramsCount;\n        token = tokens[index];\n        if (mode === 'M') mode = 'L';\n        if (mode === 'm') mode = 'l';\n      } else {\n        throw new Error('Bad segment: ' + mode);\n      }\n    } else {\n      throw new Error('Path data ended short');\n    }\n  }\n  return segments;\n}\n\nexport function serialize(segments: Segment[]): string {\n  const tokens: (string | number)[] = [];\n  for (const { key, data } of segments) {\n    tokens.push(key);\n    switch (key) {\n      case 'C':\n      case 'c':\n        tokens.push(\n          data[0],\n          `${data[1]},`,\n          data[2],\n          `${data[3]},`,\n          data[4],","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/blocksuite/affine/blocks/surface/src/utils/path-data-parser/parser.ts#L101-L137","documentation":"parsePath() converts an SVG path 'd' string into Segment objects. 'Path data ended short' is thrown when a command requires more numeric parameters than remain in the token stream — the loop guard `index + paramsCount < tokens.length` fails. Each command's required count is defined in the PARAMS table (M/L=2, C=6, Q/S=4, H/V=1, A=7, Z=0).","triggerScenarios":"Calling parsePath() with a truncated or malformed path string, e.g. 'M 10 20 C' (C expects 6 numbers, none follow), 'L 5' (L expects 2), or a string that ends mid-segment after a command token. Also triggered when a number token is missing between commands.","commonSituations":"Hand-authored or pasted SVG path data with a typo; path strings truncated during serialization, clipboard transfer, or URL encoding; dynamically built path strings via string concatenation that drop trailing coordinates.","solutions":["Verify every command is followed by exactly the count of numbers its PARAMS entry requires (A/a=7, C/c=6, Q/q/S/s=4, M/m/L/l=2, H/h/V/v/T/t=1, Z/z=0).","If constructing paths programmatically, use the module's own serialize() rather than manual string concatenation.","Run the path string through an SVG path linter/validator before passing it to parsePath()."],"exampleFix":"// before\nparsePath('M 10 20 C'); // C needs 6 numbers\n\n// after\nparsePath('M 10 20 C 30 40 50 60 70 80');","handlingStrategy":"validation","validationCode":"function paramCountsOk(d: string): boolean {\n  const PARAMS: Record<string, number> = { A:7,a:7,C:6,c:6,H:1,h:1,L:2,l:2,M:2,m:2,Q:4,q:4,S:4,s:4,T:2,t:2,V:1,v:1,Z:0,z:0 };\n  const tokens = (d.match(/[aAcChHlLmMqQsStTvVzZ]|-?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?/g) ?? []);\n  let i = 0, mode = 'M';\n  while (i < tokens.length) {\n    if (/[a-zA-Z]/.test(tokens[i])) mode = tokens[i++];\n    const need = PARAMS[mode];\n    if (need === undefined) return false;\n    for (let k = 0; k < need; k++) { if (i >= tokens.length || /a-zA-Z/.test(tokens[i])) return false; i++; }\n  }\n  return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const segments = parsePath(d);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Path data ended short') {\n    // sanitize or skip this path\n  } else throw e;\n}","preventionTips":["Round-trip paths through serialize() from the same module instead of hand-building strings","Lint persisted SVG path data on save"],"tags":["svg","path-data","parsing"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}