moeru-ai/airi · error
timeout must be a positive finite number
Error message
timeout must be a positive finite number
What it means
Thrown by the screenCapture.setSource invoke handler when request.timeout is a number but is <= 0, not finite, or NaN. Only numeric values are validated; undefined/string timeouts are passed through untouched. The timeout governs how long the source-selection mutex is held before auto-release.
Source
Thrown at packages/electron-screen-capture/src/main/index.ts:226
defineInvokeHandler(context, screenCapture.getSources, async (sourcesOptions) => {
// NOTICE(@nekomeowww): In probability of 9/10, the window thumbnail is purely empty or black, sources printed and
// nothing is returned from the desktopCapturer API.
// NOTICE(@sumimakito): Not only thumbnail is empty, the appIcon could be empty as well with nothing returned.
// REVIEW(@sumimakito): This has nothing to do with out side, probably related to Electron Bug, you can
// read more here https://github.com/electron/electron/issues/44504
const sources = await desktopCapturer.getSources(sourcesOptions)
return sources.map(source => toSerializableDesktopCapturerSource(source))
})
defineInvokeHandler(context, screenCapture.setSource, async (request, eventaOptions) => {
// FIXME: Would be better if `onlySameWindow` in `createContext` also filters out invocations here.
if (window.webContents.id !== eventaOptions?.raw.ipcMainEvent.sender.id)
return
const { timeout } = request
if (typeof timeout === 'number' && (timeout <= 0 || !Number.isFinite(timeout) || Number.isNaN(timeout))) {
throw new Error('timeout must be a positive finite number')
}
await setSourceMutex.acquire()
log.withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) }).debug('setSourceMutex acquired')
clearTimeout(setSourceMutexTimeoutHandle)
const handle = nanoid()
setSourceMutexTimeoutHandle = undefined
screenCaptureSourceMutexHandle = handle
try {
session.setDisplayMediaRequestHandler(async (_request, callback) => {
const sources = await desktopCapturer.getSources(request.options)
const source = sources.find(source => source.id === request.sourceId)
if (!source) {
throw new Error(`Source with id ${request.sourceId} not found.`)
}
View on GitHub (pinned to 27111382b4)
Solutions
- Omit timeout if you do not need a custom value, or pass a positive finite number of milliseconds.
- Validate renderer-side before invoking: if provided, ensure Number.isFinite(t) && t > 0.
- Default the UI control to a sensible minimum (e.g. 30000) and disable submit at 0.
Example fix
// before (renderer)
invoke(setSource, { sourceId, options, timeout: 0 })
// after
invoke(setSource, { sourceId, options, timeout: 30000 }) Defensive patterns
Strategy: validation
Validate before calling
function normalizeTimeout(t: unknown): number | undefined {
if (t === undefined || typeof t === 'string') return t as undefined
if (typeof t === 'number' && Number.isFinite(t) && t > 0) return t
throw new Error('timeout must be a positive finite number')
} Type guard
function isValidTimeout(t: unknown): t is number {
return typeof t === 'number' && Number.isFinite(t) && t > 0
} Try / catch
try {
await invoke(screenCapture.setSource, { sourceId, options, timeout })
} catch (error) {
if (error instanceof Error && error.message.includes('positive finite number')) {
// drop timeout and retry with default
await invoke(screenCapture.setSource, { sourceId, options })
} else throw error
} Prevention
- Validate timeout renderer-side before invoking setSource.
- Default UI controls to a positive minimum (e.g. 30000ms).
- Omit timeout entirely when the default behavior is acceptable.
When it happens
Trigger: A renderer invoking setSource({ sourceId, options, timeout: 0 }) or timeout: -1, Infinity, NaN; a UI control that computes timeout from an empty input that coerces to 0.
Common situations: Renderer passing timeout: 0 intending 'instant'; a slider bound to a number that can go to 0; JSON payload from a third-party caller with timeout as a malformed number.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Invalid Godot stage scene input payload.
- componentName is required to spawn a widget.
- id is required to update a widget.
- id is required to remove a widget.
- Failed to apply server channel configuration
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/b055fa357d02be75.
Report an issue: GitHub.