sipeed/picoclaw · warning
id is required
Error message
id is required
What it means
Returned by DELETE /api/models/catalog/{id} when r.PathValue("id") is empty. The route is registered as "DELETE /api/models/catalog/{id}" (Go 1.22 ServeMux), which matches /api/models/catalog/ with an empty segment - so in practice this error means the client built the URL with an empty id, typically fetch('/api/models/catalog/' + id) with id undefined or ''.
Source
Thrown at web/backend/api/model_catalog.go:148
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}
func (h *Handler) handleDeleteCatalog(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if id == "" {
http.Error(w, "id is required", http.StatusBadRequest)
return
}
store, err := loadCatalogs()
if err != nil {
http.Error(w, fmt.Sprintf("Failed to load catalogs: %v", err), http.StatusInternalServerError)
return
}
if _, ok := store.Entries[id]; !ok {
http.Error(w, "catalog not found", http.StatusNotFound)
return
}
delete(store.Entries, id)
if err := saveCatalogs(store); err != nil {
http.Error(w, fmt.Sprintf("Failed to save catalogs: %v", err), http.StatusInternalServerError)
returnView on GitHub (pinned to 49183d7e8d)
Solutions
- Guard the id client-side and skip the request when it is falsy
- Always use an id obtained from GET /api/models/catalog (entries carry an "id" field)
- If you see this in logs, log the failing URL to find which code path built it
Example fix
// before - id may be undefined, URL becomes /api/models/catalog/
fetch(`/api/models/catalog/${id}`, {method: 'DELETE'})
// after - guard before building the URL
if (!id) throw new Error('catalog id is required');
fetch(`/api/models/catalog/${encodeURIComponent(id)}`, {method: 'DELETE'}) Defensive patterns
Strategy: validation
Validate before calling
function assertCatalogId(id: unknown): asserts id is string {
if (typeof id !== 'string' || id.trim() === '') {
throw new Error('catalog id is required');
}
} Type guard
const isCatalogId = (v: unknown): v is string => typeof v === 'string' && v.trim() !== '';
Try / catch
if (!isCatalogId(row?.id)) return; // guard: never send DELETE with an empty id
await fetch(`/api/models/catalog/${encodeURIComponent(row.id)}`, {method: 'DELETE'}); Prevention
- Guard falsy ids before building the URL - undefined renders as the bare catalog/ path
- Always encodeURIComponent the id - it contains '|' separators
- Source ids only from GET /api/models/catalog entries
When it happens
Trigger: Template-literal fetch with an undefined variable: `/api/models/catalog/${id}` when id is undefined renders as the bare catalog/ path; a raw DELETE to /api/models/catalog/; string concatenation producing a trailing slash.
Common situations: UI delete buttons firing before the row's id loaded; refactoring that renames the id variable; stale client code after the API changed shape.
Related errors
- Invalid index
- build request: %w
- matrix room ID is empty: %w
- Invalid JSON: %v
- port %d is out of range (1-65535)
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/3a9297bf76ea0aea.
Report an issue: GitHub.