tonhowtf/omniget · warning
[player] failed saving auto_skip_on_error
Error message
[player] failed saving auto_skip_on_error
What it means
MusicPlayerStore logs this warning when persisting the auto_skip_on_error playback setting to the Tauri 'study' plugin backend via study:music:player:settings:set fails. The in-memory value (autoSkipOnError) is still applied, so playback behaves as requested for the session, but the preference is not saved. It is a non-fatal durability failure, not a playback failure.
Solutions
- Check the backend exposes the study:music:player:settings:set command and that the 'study' plugin is registered and allowed in the Tauri capability/allowlist config.
- Inspect the logged error object `e` to distinguish missing-command vs IPC/permission failures.
- Verify the payload key auto_skip_on_error is a plain boolean (serialization mismatches cause backend rejections).
- Queue a retry or re-sync settings on app start so the preference is eventually persisted.
Example fix
// before
await pluginInvoke("study", "study:music:player:settings:set", { auto_skip_on_error: value });
// after
await pluginInvoke("study", "study:music:player:settings:set", { auto_skip_on_error: Boolean(value) }); // plus ensure command exists and capability allows study:player:settings Defensive patterns
Strategy: try-catch
Validate before calling
// ensure backend command exists before saving
const supported = await pluginInvoke<boolean>("study", "study:music:player:settings:has").catch(() => false); Type guard
function isSettingsPayload(v: unknown): v is { auto_skip_on_error: boolean } {
return typeof v === "object" && v !== null && typeof (v as any).auto_skip_on_error === "boolean";
} Try / catch
try {
await pluginInvoke("study", "study:music:player:settings:set", { auto_skip_on_error: Boolean(value) });
} catch (e) {
console.warn("[player] failed saving auto_skip_on_error", e);
this.pendingSettingSync = true; // retry on next app start
} Prevention
- Keep frontend command names in sync with backend registrations via a shared constants module.
- Add Tauri capability/permission entries whenever adding plugin commands.
- Re-sync settings from backend on startup to detect unpersisted values.
- Serialize booleans explicitly (Boolean(value)) before IPC.
When it happens
Trigger: Calling the store's auto-skip-on-error setter with a boolean and the pluginInvoke('study','study:music:player:settings:set',{auto_skip_on_error:value}) call rejecting — backend command missing, plugin not registered, IPC failure, or backend rejecting the payload.
Common situations: Running the frontend against a backend build without the settings:set command; Tauri plugin permission for the 'study' plugin not granted; backend restarting or connection dropped mid-call; typo'd/renamed command after a refactor.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- close failed
- create notebook failed
- e.to_string()
- notebooksStore.hydrate failed
- notebooksStore.refresh failed
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/bc156e94dee64825.
Report an issue: GitHub.
Appendix: source
Thrown at src/lib/study-music/player-store.svelte.ts:522
this.playerSettingsLoaded = true;
try {
const res = await pluginInvoke<{
auto_skip_on_error?: boolean;
}>("study", "study:music:player:settings:get", {});
this.autoSkipOnError = !!res?.auto_skip_on_error;
} catch (e) {
console.warn("[player] failed loading player settings", e);
}
}
async setAutoSkipOnError(value: boolean) {
this.autoSkipOnError = value;
try {
await pluginInvoke("study", "study:music:player:settings:set", {
auto_skip_on_error: value,
});
} catch (e) {
console.warn("[player] failed saving auto_skip_on_error", e);
}
}
private setError(err: PlayerError) {
this.lastError = err;
this.error = err.message || null;
this.status = "error";
this.loading = false;
if (this.autoSkipOnError && !err.retriable) {
const skipDelay = 300;
setTimeout(() => {
if (this.lastError && this.lastError.trackId === err.trackId) {
this.skipOnError();
}
}, skipDelay);
}
}
View on GitHub (pinned to 8600b91f42)