tonhowtf/omniget · error
create notebook failed
Error message
create notebook failed
What it means
notebooksStore.create() calls notesNotebooksCreate (Tauri command 'study:notes:notebooks:create') and then refresh(). If creation rejects, the catch logs 'create notebook failed' and the promise resolves to null, which callers must treat as 'no notebook created'. Backend validation (empty name, duplicates), DB write errors, or IPC failure all surface here.
Solutions
- Read the logged error to distinguish validation rejection from IPC/DB failure.
- Validate the name (non-empty, length/charset limits) in the UI before calling create().
- Confirm 'study:notes:notebooks:create' is registered and allowed in capabilities.
- Handle the null return at call sites so the user gets feedback instead of a silent no-op.
- If the error came from refresh(), re-run refresh() — the notebook may already exist.
Example fix
// before
const id = await notebooksStore.create({ name: nameInput.trim() });
if (id === null) return; // silent failure
// after
const name = nameInput.trim();
if (!name) { showError("Notebook name is required"); return; }
const id = await notebooksStore.create({ name });
if (id === null) showError("Could not create notebook — check the console for details"); Defensive patterns
Strategy: validation
Validate before calling
function validateNotebookName(name: string): string | null {
const n = name.trim();
if (!n) return 'Name is required';
if (n.length > 100) return 'Name too long (max 100 chars)';
return null;
}
const err = validateNotebookName(input);
if (err) { showError(err); return; } Type guard
function isCreatedId(r: { notebook_id: number } | null): r is { notebook_id: number } {
return r !== null && typeof r.notebook_id === 'number';
} Try / catch
const id = await notebooksStore.create({ name: trimmedName });
if (id === null) {
showError('Notebook creation failed — see console for details');
return;
} Prevention
- Always trim and validate names before calling create()
- Treat null return as failure and inform the user — it never throws
- Check backend name uniqueness/length rules and mirror them client-side
- If null but refresh shows the notebook, handle the refresh-race case
- Test create against the real backend, not just the happy-path mock
When it happens
Trigger: notesNotebooksCreate(args) rejects: backend rejects the name (empty/too long/invalid characters), the notes DB write fails, the plugin command is unavailable, or the Tauri IPC call fails; also fires if the subsequent refresh() throws, though the notebook was actually created in that case.
Common situations: User submits an empty or whitespace-only notebook name; name collides with backend uniqueness rules; DB locked or disk full; capability missing for the create command; running in a non-Tauri environment.
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/7211f41ec34be763.
Report an issue: GitHub.
Appendix: source
Thrown at src/lib/study-notes/notebooks-store.svelte.ts:110
this.activeId = notebookId;
try {
await notesNotebooksActiveSet(notebookId);
} catch (e) {
console.warn("setActive failed", e);
}
}
async create(args: {
name: string;
color?: string | null;
iconLucide?: string | null;
}): Promise<number | null> {
try {
const r = await notesNotebooksCreate(args);
await this.refresh();
return r.notebook_id;
} catch (e) {
console.warn("create notebook failed", e);
return null;
}
}
async rename(notebookId: number, newName: string) {
try {
await notesNotebooksRename({ notebookId, newName });
await this.refresh();
} catch (e) {
console.warn("rename failed", e);
}
}
async close(notebookId: number) {
try {
await notesNotebooksClose(notebookId);
if (this.activeId === notebookId) {
await this.setActive(1);View on GitHub (pinned to 8600b91f42)