toeverything/AFFiNE · error

Invalid duration string unit ${ch}

Error message

Invalid duration string unit ${ch}

What it means

Thrown by the parse() function in the duration utility when it encounters a character that is not a digit (0-9) and not a recognized duration unit letter. Recognized unit letters are: d (day), w (week), M (month), y (year), h (hour), m (minute), s (second), and the special case 'ms' (milliseconds). Any other character after a number triggers this error.

Source

Thrown at packages/backend/server/src/base/utils/duration.ts:39

  109: 'm',
  115: 's',
};

function parse(str: string): DurationInput {
  let input: DurationInput = {};

  let acc = 0;
  for (let i = 0; i < str.length; i++) {
    const ch = str[i];
    const code = ch.charCodeAt(0);

    // number [0..9]
    if (code >= 48 && code <= 57) {
      acc = acc * 10 + code - 48;
    } else {
      let unit = KnownCharCodeToCharMap[code];
      if (!unit) {
        throw new Error(`Invalid duration string unit ${ch}`);
      }

      // look ahead a char for 'ms' checking if unit met 'm'
      if (unit === 'm' && str[i + 1] === 's') {
        unit = 'ms';
        i++;
      }

      input[unit] = acc;
      acc = 0;
    }
  }

  return input;
}

export const Due = {
  ms: (dueStr: string | DurationInput) => {

View on GitHub (pinned to 26c515e050)

Solutions

  1. Use single-letter unit abbreviations: d, w, M (month), y, h, m (minute), s, ms.
  2. Remember the parser is case-sensitive: 'M' = month, 'm' = minute, 's' = second, 'ms' = millisecond.
  3. Validate duration strings before passing them to the Due functions, or wrap calls in try-catch.
  4. Alternatively, pass a DurationInput object directly instead of a string (e.g. { h: 1, m: 30 }).

Example fix

// before
Due.ms('5min'); // 'min' -> 'n' is not a unit -> throws
Due.ms('2H'); // uppercase H not recognized -> throws

// after
Due.ms('5m'); // 5 minutes
Due.ms('2h'); // 2 hours
// or pass an object
Due.ms({ minutes: 5 }); // note: key is 'm' not 'minutes'
Defensive patterns

Strategy: validation

Validate before calling

const VALID_UNITS = /^[\d]+(ms|[dwMyhms])/;
function isValidDuration(str: string): boolean {
  // split into number-unit pairs and validate each
  return str.split(/(?<=\D)(?=\d)/).every(part => VALID_UNITS.test(part));
}
if (!isValidDuration(durationStr)) {
  throw new Error(`Invalid duration string: ${durationStr}`);
}
Due.ms(durationStr);

Type guard

const isParsableDuration = (str: string): boolean => {
  try {
    Due.parse(str);
    return true;
  } catch {
    return false;
  }
};

Try / catch

try {
  const ms = Due.ms(durationStr);
} catch (e) {
  if (e.message?.includes('Invalid duration string unit')) {
    // parse failed; use a default duration or prompt user
    const ms = Due.ms('1h'); // fallback
  }
}

Prevention

When it happens

Trigger: Calling Due.ms(str), Due.s(str), Due.after(str), or Due.before(str) with a duration string containing an invalid unit character. For example: '5x' (x is not a unit), '10sec' (sec is not recognized — only 's' is), '3min' (min is not recognized — only 'm' is).

Common situations: Passing human-readable duration strings with full word units ('minutes', 'hours', 'days') instead of single-letter abbreviations. Using uppercase letters incorrectly (the parser is case-sensitive: 'M' is month, 'm' is minute, but 'H' or 'S' are not recognized). Typo in a duration string from config or user input.

Related errors


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