{"record":{"id":"b055fa357d02be75","repo":"moeru-ai/airi","slug":"timeout-must-be-a-positive-finite-number","errorCode":null,"errorMessage":"timeout must be a positive finite number","messagePattern":"timeout must be a positive finite number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/electron-screen-capture/src/main/index.ts","lineNumber":226,"sourceCode":"\n  defineInvokeHandler(context, screenCapture.getSources, async (sourcesOptions) => {\n    // NOTICE(@nekomeowww): In probability of 9/10, the window thumbnail is purely empty or black, sources printed and\n    // nothing is returned from the desktopCapturer API.\n    // NOTICE(@sumimakito): Not only thumbnail is empty, the appIcon could be empty as well with nothing returned.\n    // REVIEW(@sumimakito): This has nothing to do with out side, probably related to Electron Bug, you can\n    // read more here https://github.com/electron/electron/issues/44504\n    const sources = await desktopCapturer.getSources(sourcesOptions)\n    return sources.map(source => toSerializableDesktopCapturerSource(source))\n  })\n\n  defineInvokeHandler(context, screenCapture.setSource, async (request, eventaOptions) => {\n    // FIXME: Would be better if `onlySameWindow` in `createContext` also filters out invocations here.\n    if (window.webContents.id !== eventaOptions?.raw.ipcMainEvent.sender.id)\n      return\n\n    const { timeout } = request\n    if (typeof timeout === 'number' && (timeout <= 0 || !Number.isFinite(timeout) || Number.isNaN(timeout))) {\n      throw new Error('timeout must be a positive finite number')\n    }\n\n    await setSourceMutex.acquire()\n    log.withFields({ windowId, windowTitle: tryWindowTitle(window, windowTitle) }).debug('setSourceMutex acquired')\n\n    clearTimeout(setSourceMutexTimeoutHandle)\n    const handle = nanoid()\n    setSourceMutexTimeoutHandle = undefined\n    screenCaptureSourceMutexHandle = handle\n\n    try {\n      session.setDisplayMediaRequestHandler(async (_request, callback) => {\n        const sources = await desktopCapturer.getSources(request.options)\n        const source = sources.find(source => source.id === request.sourceId)\n        if (!source) {\n          throw new Error(`Source with id ${request.sourceId} not found.`)\n        }\n","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/electron-screen-capture/src/main/index.ts#L208-L244","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (renderer)\ninvoke(setSource, { sourceId, options, timeout: 0 })\n// after\ninvoke(setSource, { sourceId, options, timeout: 30000 })","handlingStrategy":"validation","validationCode":"function normalizeTimeout(t: unknown): number | undefined {\n  if (t === undefined || typeof t === 'string') return t as undefined\n  if (typeof t === 'number' && Number.isFinite(t) && t > 0) return t\n  throw new Error('timeout must be a positive finite number')\n}","typeGuard":"function isValidTimeout(t: unknown): t is number {\n  return typeof t === 'number' && Number.isFinite(t) && t > 0\n}","tryCatchPattern":"try {\n  await invoke(screenCapture.setSource, { sourceId, options, timeout })\n} catch (error) {\n  if (error instanceof Error && error.message.includes('positive finite number')) {\n    // drop timeout and retry with default\n    await invoke(screenCapture.setSource, { sourceId, options })\n  } else throw error\n}","preventionTips":["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."],"tags":["electron","screen-capture","ipc","validation"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}