sipeed/picoclaw · error
Failed to load catalogs: %v
Error message
Failed to load catalogs: %v
What it means
Returned by GET /api/models/catalog when loadCatalogs() (model_catalog.go:72-89) fails to read or parse model_catalogs.json under config.GetHome(). A missing file is fine (returns an empty store); errors require a read failure (permissions, I/O) or invalid JSON in the file - e.g. truncated by an external editor or non-atomic writer, since the API's own saves go through WriteFileAtomic and cannot leave half-written files.
Source
Thrown at web/backend/api/model_catalog.go:126
provider = providers.NormalizeProvider(provider)
store.Entries[key] = &CatalogEntry{
ID: key,
Provider: provider,
APIBase: strings.TrimRight(strings.TrimSpace(apiBase), "/"),
APIKeyMask: maskAPIKeyValue(apiKey),
Models: models,
FetchedAt: time.Now().UTC().Format(time.RFC3339),
}
return saveCatalogs(store)
}
// handleListCatalogs returns all saved model catalogs.
//
// GET /api/models/catalog
func (h *Handler) handleListCatalogs(w http.ResponseWriter, r *http.Request) {
store, err := loadCatalogs()
if err != nil {
http.Error(w, fmt.Sprintf("Failed to load catalogs: %v", err), http.StatusInternalServerError)
return
}
entries := make([]*CatalogEntry, 0, len(store.Entries))
for _, e := range store.Entries {
entries = append(entries, e)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{
"entries": entries,
"total": len(entries),
})
}
// handleDeleteCatalog deletes a saved model catalog by ID.
//
// DELETE /api/models/catalog/{id}View on GitHub (pinned to 49183d7e8d)
Solutions
- Check the wrapped error to distinguish parse failure from read failure
- Fix the JSON, or delete model_catalogs.json - it is a cache; catalogs are recreated the next time models are fetched per provider/key
- Restore read permission for the backend user
- Prefer DELETE /api/models/catalog/{id} for pruning so the file is only ever rewritten atomically by the API
Defensive patterns
Strategy: try-catch
Try / catch
const res = await fetch('/api/models/catalog');
if (res.status === 500) {
const detail = await res.text();
// model_catalogs.json is a rebuildable cache - offer reset and degrade gracefully
if (confirm('Saved catalogs file is unreadable (' + detail + '). Reset it?')) {
await resetCatalogFile(); // delete model_catalogs.json server-side
return (await fetch('/api/models/catalog')).json();
}
return {entries: [], total: 0};
} Prevention
- Never edit model_catalogs.json by hand - use the catalog endpoints so writes stay atomic
- Treat the catalog store as disposable cache: refetching models rebuilds it
- Alert on repeated 500s here; it signals filesystem trouble in the config home
When it happens
Trigger: model_catalogs.json hand-edited and left invalid; the file truncated by a crash in an external tool; read permission removed; the home/config directory unavailable (network home, unmounted volume).
Common situations: Users pruning catalog entries by hand; backup restores of a partially-written file; multi-process access with a non-atomic external writer.
Related errors
- Failed to save catalogs: %v
- create output dir: %w
- marshal result: %w
- write result: %w
- marshal request: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/20aba93225733cf0.
Report an issue: GitHub.