toeverything/AFFiNE · error · Error

Path data ended short

Error message

Path data ended short

What it means

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).

Source

Thrown at blocksuite/affine/blocks/surface/src/utils/path-data-parser/parser.ts:119

          params[params.length] = +numbeToken.text;
        } else {
          throw new Error(
            'Param not a number: ' + mode + ',' + numbeToken.text
          );
        }
      }
      if (typeof PARAMS[mode] === 'number') {
        const segment: Segment = { key: mode, data: params };
        segments.push(segment);
        index += paramsCount;
        token = tokens[index];
        if (mode === 'M') mode = 'L';
        if (mode === 'm') mode = 'l';
      } else {
        throw new Error('Bad segment: ' + mode);
      }
    } else {
      throw new Error('Path data ended short');
    }
  }
  return segments;
}

export function serialize(segments: Segment[]): string {
  const tokens: (string | number)[] = [];
  for (const { key, data } of segments) {
    tokens.push(key);
    switch (key) {
      case 'C':
      case 'c':
        tokens.push(
          data[0],
          `${data[1]},`,
          data[2],
          `${data[3]},`,
          data[4],

View on GitHub (pinned to 26c515e050)

Solutions

  1. 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).
  2. If constructing paths programmatically, use the module's own serialize() rather than manual string concatenation.
  3. Run the path string through an SVG path linter/validator before passing it to parsePath().

Example fix

// before
parsePath('M 10 20 C'); // C needs 6 numbers

// after
parsePath('M 10 20 C 30 40 50 60 70 80');
Defensive patterns

Strategy: validation

Validate before calling

function paramCountsOk(d: string): boolean {
  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 };
  const tokens = (d.match(/[aAcChHlLmMqQsStTvVzZ]|-?\d*\.?\d+(?:[eE][-+]?\d+)?/g) ?? []);
  let i = 0, mode = 'M';
  while (i < tokens.length) {
    if (/[a-zA-Z]/.test(tokens[i])) mode = tokens[i++];
    const need = PARAMS[mode];
    if (need === undefined) return false;
    for (let k = 0; k < need; k++) { if (i >= tokens.length || /a-zA-Z/.test(tokens[i])) return false; i++; }
  }
  return true;
}

Try / catch

try {
  const segments = parsePath(d);
} catch (e) {
  if (e instanceof Error && e.message === 'Path data ended short') {
    // sanitize or skip this path
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/46bb0fcb907dae40. Report an issue: GitHub.