moeru-ai/airi · error · Error

id is required to remove a widget.

Error message

id is required to remove a widget.

What it means

Thrown by executeWidgetAction when action='remove' but no widget id can be resolved. The guard at widgets.ts:291 mirrors the update guard: normalizedId (trimmed id) must be truthy because removeWidget IPC targets exactly one widget by id. Unlike 'clear', 'remove' cannot operate without an identifier.

Source

Thrown at apps/stage-tamagotchi/src/renderer/stores/tools/builtin/widgets.ts:291

    case 'update': {
      if (!normalizedId)
        throw new Error('id is required to update a widget.')

      const componentProps = normalizeComponentProps(input.componentProps)
      const sanitizedComponentProps = sanitizeComponentPropsForDispatch(input.componentName, componentProps)
      const windowSize = resolveWindowSize(input.componentName, sanitizedComponentProps, input.windowSize)
      await invokers.updateWidget({
        id: normalizedId,
        componentProps: sanitizedComponentProps,
        ...(input.alwaysOnTop === undefined ? {} : { alwaysOnTop: input.alwaysOnTop }),
        ...(windowSize === undefined ? {} : { windowSize }),
      })

      return `Updated widget (${normalizedId}).`
    }
    case 'remove': {
      if (!normalizedId)
        throw new Error('id is required to remove a widget.')

      await invokers.removeWidget({ id: normalizedId })
      return `Removed widget (${normalizedId}).`
    }
    case 'clear': {
      await invokers.clearWidgets()
      return 'Cleared all widgets.'
    }
    case 'open': {
      const id = await invokers.prepareWindow(normalizedId ? { id: normalizedId } : {})
      await invokers.openWindow(normalizedId ? { id: normalizedId } : {})
      return `Opened widget window${id ? ` (${id})` : ''}.`
    }
    default:
      return 'No action performed.'
  }
}

View on GitHub (pinned to 27111382b4)

Solutions

  1. Pass the specific widget id to remove.
  2. If the intent is to remove all widgets, use action: 'clear' instead, which takes no id.
  3. Track spawned widget ids in caller state so remove always has a concrete target.

Example fix

// before
await executeWidgetAction({ action: 'remove', id: '' })
// after
await executeWidgetAction({ action: 'remove', id: 'w1' })
Defensive patterns

Strategy: validation

Validate before calling

const normalizedId = input.id?.trim()
if (input.action === 'remove' && !normalizedId)
  throw new Error('id is required to remove a widget.')
// if intending to remove all, use action: 'clear'

Type guard

function hasRemoveId(input) {
  return input.action !== 'remove' || Boolean(input.id?.trim())
}

Try / catch

try {
  await executeWidgetAction(input)
} catch (e) {
  if (e.message.includes('id is required to remove'))
    // either supply the id or switch to action: 'clear'
  throw e
}

Prevention

When it happens

Trigger: Calling executeWidgetAction({ action: 'remove' }) with id omitted, empty, or whitespace. An LLM tool-call selecting remove but failing to carry the widget id forward.

Common situations: Confusing 'remove' (single widget by id) with 'clear' (all widgets) and omitting id; losing the id between spawn and remove turns in an agent conversation.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/f1768dd918d818f8. Report an issue: GitHub.