CherryHQ/cherry-studio · warning · Error

Invalid duration: "${duration}". Use formats like '30m', '2h

Error message

Invalid duration: "${duration}". Use formats like '30m', '2h', '1h30m'.

What it means

parseDurationToMinutes throws when the input matches neither /(\d+)\s*h/i nor /(\d+)\s*m/i, AND parseInt(duration, 10) is NaN or <= 0. It is used to parse the cron tool's 'every' interval field. Accepted forms are like '30m', '2h', '1h30m', or a bare positive integer of minutes.

Source

Thrown at src/main/ai/mcp/servers/cherryAutonomyTools.ts:59

   */
  getKnowledgeBaseIds: () => string[]
}

/**
 * Parse a human-friendly duration string (e.g. '30m', '2h', '1h30m') into minutes.
 */
function parseDurationToMinutes(duration: string): number {
  let totalMinutes = 0
  const hourMatch = duration.match(/(\d+)\s*h/i)
  const minMatch = duration.match(/(\d+)\s*m/i)

  if (hourMatch) totalMinutes += parseInt(hourMatch[1], 10) * 60
  if (minMatch) totalMinutes += parseInt(minMatch[1], 10)

  if (totalMinutes === 0) {
    const raw = parseInt(duration, 10)
    if (!isNaN(raw) && raw > 0) return raw
    throw new Error(`Invalid duration: "${duration}". Use formats like '30m', '2h', '1h30m'.`)
  }

  return totalMinutes
}

const CRON_TOOL: Tool = {
  name: CRON_TOOL_NAME,
  description:
    "Manage scheduled tasks. Use action 'add' to create a recurring or one-time job, 'list' to see all jobs, or 'remove' to delete a job. For one-time jobs, use the 'at' field with an RFC3339 timestamp.",
  inputSchema: {
    type: 'object',
    properties: {
      action: {
        type: 'string',
        enum: ['add', 'list', 'remove'],
        description: 'The action to perform'
      },
      name: {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Use only hours/minutes forms: '30m', '2h', '1h30m', or a positive integer minute count.
  2. Convert seconds/days beforehand: 90s -> '2m' (round up), 1d -> '24h'.
  3. Strip surrounding text and validate against /^(\d+\s*h)?(\s*\d+\s*m)?$/i before calling.
  4. For one-shot times use 'at' with an RFC3339 timestamp instead; for cron schedules use 'cron'.

Example fix

// before — unsupported unit
addJob({ every: '1d', ... })

// after — express in supported h/m units
addJob({ every: '24h', ... })
// or validate first
function isValidEvery(v: string) { return /^\s*(\d+\s*h)?(\s*\d+\s*m)?\s*$/i.test(v) && /\d/.test(v) }
Defensive patterns

Strategy: validation

Validate before calling

function validateEvery(v: unknown): string {
  if (typeof v !== 'string') throw new Error('every must be a string')
  const trimmed = v.trim()
  const ok = /^(\d+\s*h)?(\s*\d+\s*m)?$/i.test(trimmed) && /\d/.test(trimmed)
  if (!ok) {
    const n = parseInt(trimmed, 10)
    if (isNaN(n) || n <= 0) throw new Error(`Invalid duration: "${v}". Use '30m', '2h', '1h30m'.`)
    return `${n}m`
  }
  return trimmed.replace(/\s+/g, '')
}

Type guard

function isValidDuration(v: unknown): v is string {
  if (typeof v !== 'string') return false
  const t = v.trim()
  if (/^(\d+\s*h)?(\s*\d+\s*m)?$/i.test(t) && /\d/.test(t)) return true
  const n = parseInt(t, 10)
  return !isNaN(n) && n > 0
}

Try / catch

try {
  const minutes = parseDurationToMinutes(validateEvery(args.every))
} catch (e) {
  return { content: [{ type: 'text', text: e.message }], isError: true }
}

Prevention

When it happens

Trigger: Passing 'every' with an unsupported unit: '1d', '45s', '500ms', 'weekly', negative numbers, zero, empty string, or pure non-numeric text.

Common situations: Agent or user assumes ISO-8601 durations (P1D) or seconds are supported; passes '0' or '-5'; copies a cron expression into the every field; includes extra spaces or words like 'every 30m'.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/cdb9645b942e1c6f. Report an issue: GitHub.