tonhowtf/omniget · warning
notebooksStore.refresh failed
Error message
notebooksStore.refresh failed
What it means
notebooksStore.refresh() reloads the notebook list via notesNotebooksList({includeClosed:true}), a Tauri pluginInvoke of 'study:notes:notebooks:list'. On rejection the catch logs 'notebooksStore.refresh failed' and leaves this.list stale. All mutating store actions (create, rename, close, reopen, delete, movePage) call refresh afterwards, so a failed refresh shows outdated notebook state even when the mutation itself succeeded.
Solutions
- Inspect the logged rejection reason from notesNotebooksList to identify the backend error.
- Verify 'study:notes:notebooks:list' exists and is allowed in Tauri capabilities.
- Test the bridge call directly from the console to isolate store vs backend.
- If the DB is locked or corrupted, resolve the writer contention or repair the notes DB.
- Make UI affordances call refresh() again (retry) since the swallowed error leaves this.list stale.
Example fix
// before
async refresh() {
try {
this.list = await notesNotebooksList({ includeClosed: true });
} catch (e) {
console.warn("notebooksStore.refresh failed", e);
}
}
// after
async refresh() {
try {
this.list = await notesNotebooksList({ includeClosed: true });
} catch (e) {
console.warn("notebooksStore.refresh failed", e);
this.refreshFailed = true; // surface staleness to the UI for retry
}
} Defensive patterns
Strategy: retry
Validate before calling
// guard before treating list as authoritative if (!notebooksStore.hydrated) await notebooksStore.hydrate(); const count = notebooksStore.list.length;
Type guard
function hasErrorReason(e: unknown): e is Error {
return e instanceof Error;
} Try / catch
let lastErr: unknown;
for (let i = 0; i < 3; i++) {
await notebooksStore.refresh();
if (notebooksStore.list.length > 0) break;
lastErr = new Error('refresh returned empty');
await new Promise((r) => setTimeout(r, 300 * (i + 1)));
} Prevention
- Re-call refresh() after any mutation failure to resync UI state
- Track a refreshFailed/staleAt timestamp so the UI can show staleness
- Keep bridge command names in sync with backend command registry
- Add retries with backoff for list-type reads
- Monitor console warnings in dev builds to catch backend regressions early
When it happens
Trigger: notesNotebooksList rejects: backend command error, notes DB unavailable, plugin/capability misconfiguration, or IPC failure; it runs after every successful mutation, so it also fires when only the follow-up list query fails.
Common situations: Backend list endpoint broken after a schema migration; DB locked by another writer; command renamed on the backend but not the bridge; running outside the Tauri shell so pluginInvoke rejects; transient IPC failure right after a mutation.
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
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/ff3e659100b079ce.
Report an issue: GitHub.
Appendix: source
Thrown at src/lib/study-notes/notebooks-store.svelte.ts:78
const [rows, active] = await Promise.all([
notesNotebooksList({ includeClosed: true }),
notesNotebooksActiveGet(),
]);
this.list = rows;
this.activeId = active.notebook_id;
} catch (e) {
console.warn("notebooksStore.hydrate failed", e);
} finally {
this.hydrated = true;
this.loading = false;
}
}
async refresh() {
try {
this.list = await notesNotebooksList({ includeClosed: true });
} catch (e) {
console.warn("notebooksStore.refresh failed", e);
}
}
pageCountOf(id: number): number {
return this.list.find((n) => n.id === id)?.page_count ?? 0;
}
byId(id: number): Notebook | null {
return this.list.find((n) => n.id === id) ?? null;
}
async setActive(notebookId: number) {
if (this.activeId === notebookId) return;
this.activeId = notebookId;
try {
await notesNotebooksActiveSet(notebookId);
} catch (e) {
console.warn("setActive failed", e);View on GitHub (pinned to 8600b91f42)