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)
		return

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Guard the id client-side and skip the request when it is falsy
  2. Always use an id obtained from GET /api/models/catalog (entries carry an "id" field)
  3. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/3a9297bf76ea0aea. Report an issue: GitHub.