toeverything/AFFiNE · error

A curve must have at least three points.

Error message

A curve must have at least three points.

What it means

After enum handling, the validator compares the runtime type of the value (via inputType — objects/arrays/null become 'json') with the schema-declared field type; a mismatch throws with both the expected and received type names. undefined values are dropped earlier and null is only allowed for optional fields, so this fires on present, non-null values of the wrong JS type.

Solutions

  1. Coerce the value to the declared type before writing (Number(id), String(x))
  2. Fix the schema declaration if it was wrong (e.g. the field genuinely holds string ids, declare type: 'string')
  3. Validate at the boundary: a small typeof-per-schema-field check before the write gives a clearer stack

Example fix

// before
table.insert({ data: { id: 'a1', count: '42' } }); // schema: count is 'number'

// after
table.insert({ data: { id: 'a1', count: 42 } });
// or coerce at the boundary: { ...dto, count: Number(dto.count) }
Defensive patterns

Strategy: type-guard

Validate before calling

null

Type guard

function matchesFieldType(value: unknown, type: FieldType): boolean {
  const actual =
    value === null || Array.isArray(value) || typeof value === 'object' ? 'json' : typeof value;
  return actual === type;
}

// before writing:
for (const [key, field] of Object.entries(table.schema)) {
  if (data[key] !== undefined && data[key] !== null && !matchesFieldType(data[key], field.type)) {
    throw new Error(`${key}: expected ${field.type}, got ${typeof data[key]}`);
  }
}

Prevention

When it happens

Trigger: Inserting a string into a field declared type: 'number' (id: '123' from a URL param); booleans into 'string' fields; Date objects into 'string' fields; arrays/objects into scalar fields while the schema says 'string' rather than 'json'.

Common situations: URL/query params and form inputs yielding strings; JSON round-trips losing number types; schema types written to match one layer while another layer produces different runtime types.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/ac8358f47d00bcad. Report an issue: GitHub.

Appendix: source

Thrown at blocksuite/affine/blocks/surface/src/utils/points-on-curve/curve-to-bezier.ts:13

import type { Point } from '../rough/geometry.js';

function clone(p: Point): Point {
  return [...p] as Point;
}

export function curveToBezier(
  pointsIn: readonly Point[],
  curveTightness = 0
): Point[] {
  const len = pointsIn.length;
  if (len < 3) {
    throw new Error('A curve must have at least three points.');
  }
  const out: Point[] = [];
  if (len === 3) {
    out.push(
      clone(pointsIn[0]),
      clone(pointsIn[1]),
      clone(pointsIn[2]),
      clone(pointsIn[2])
    );
  } else {
    const points: Point[] = [];
    points.push(pointsIn[0], pointsIn[0]);
    for (let i = 1; i < pointsIn.length; i++) {
      points.push(pointsIn[i]);
      if (i === pointsIn.length - 1) {
        points.push(pointsIn[i]);
      }
    }

View on GitHub (pinned to b4c8548c09)