{"record":{"id":"8ab182fd70db5582","repo":"tonhowtf/omniget","slug":"omnidisc-stream-volume-failed","errorCode":null,"errorMessage":"[omnidisc] stream volume failed","messagePattern":"\\[omnidisc\\] stream volume failed","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/lib/stores/omnidisc-stream-store.svelte.ts","lineNumber":299,"sourceCode":"    busy = false;\n  }\n}\n\nexport async function unwatchStream(userId: string): Promise<void> {\n  try {\n    await invoke(\"omnidisc_stream_unwatch\", { userId });\n  } catch (e) {\n    console.warn(\"[omnidisc] unwatch failed\", errorText(e));\n  } finally {\n    watchingIds = watchingIds.filter((id) => id !== userId);\n  }\n}\n\nexport async function setStreamVolume(userId: string, gain: number): Promise<void> {\n  try {\n    await invoke(\"omnidisc_stream_set_volume\", { userId, gain: Math.min(2, Math.max(0, gain)) });\n  } catch (e) {\n    console.warn(\"[omnidisc] stream volume failed\", errorText(e));\n  }\n}\n\nexport async function refreshStreamStats(): Promise<StreamStats | null> {\n  try {\n    stats = await invoke<StreamStats>(\"omnidisc_stream_stats\");\n    if (stats.publishing) publishing = stats.publishing;\n    return stats;\n  } catch {\n    return null;\n  }\n}\n\ninterface StreamEventPayload {\n  type: string;\n  audio?: AudioMode;\n  user_id?: string;\n  active?: boolean;","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src/lib/stores/omnidisc-stream-store.svelte.ts#L281-L317","documentation":"setStreamVolume invokes `omnidisc_stream_set_volume` with a gain clamped to [0, 2]. If the backend rejects (unknown user, no active audio sink for that user, command panic), the warning is logged and the volume simply is not applied. No state changes occur in the catch path.","triggerScenarios":"Calling setStreamVolume(userId, gain) for a userId with no active watched stream/audio element in the backend, a NaN gain (NaN bypasses min/max clamping), or a backend that has torn down the audio sink for that peer.","commonSituations":"Moving a volume slider while the remote stream just ended; volume applied before the watch handshake completes; NaN produced by an uncontrolled input parsed to a number.","solutions":["Guard the call: only invoke when the backend reports the user as watched (check stream stats or watchingIds and a live-sink flag).","Validate gain is a finite number before clamping: Number.isFinite(gain).","Re-apply volume after watch completes (retry once the stream is established).","Inspect the backend error to confirm the audio sink exists for that userId."],"exampleFix":"// before\nawait invoke(\"omnidisc_stream_set_volume\", { userId, gain: Math.min(2, Math.max(0, gain)) });\n// after\nif (!Number.isFinite(gain)) return;\nawait invoke(\"omnidisc_stream_set_volume\", { userId, gain: Math.min(2, Math.max(0, gain)) });","handlingStrategy":"validation","validationCode":"if (!Number.isFinite(gain) || gain < 0 || gain > 2) return;\nif (!watchingIds.includes(userId)) return;","typeGuard":"function isValidGain(g: unknown): g is number {\n  return typeof g === 'number' && Number.isFinite(g) && g >= 0 && g <= 2;\n}","tryCatchPattern":"try {\n  await invoke(\"omnidisc_stream_set_volume\", { userId, gain: clamp(gain, 0, 2) });\n} catch (e) {\n  console.warn(\"[omnidisc] stream volume failed\", errorText(e));\n  scheduleVolumeRetry(userId, gain); // re-apply once stream is live\n}","preventionTips":["Clamp and validate gain (reject NaN) before invoking.","Only allow volume changes for users with an active audio sink.","Re-apply the desired volume after watch completes, since early sets can fail.","Ignore slider events for streams that just ended."],"tags":["tauri","ipc","audio","volume"],"backgroundTag":"api-request-failed","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}