lfnovo/open-notebook · warning · HTTPException
Invalid order_by field: '{order_by}'. Allowed fields: {', '.
Error message
Invalid order_by field: '{order_by}'. Allowed fields: {', '.join(sorted(allowed_fields))} What it means
400 from GET /api/v1/notebooks when the single-word order_by query param's field is not in {name, created, updated}. The router validates order_by manually: lowercased, split on whitespace, first token must be an allowed field.
Source
Thrown at api/routers/notebooks.py:75
last_viewed_at=str(row.get("last_viewed_at", "")),
)
@router.get("/notebooks", response_model=List[NotebookResponse])
async def get_notebooks(
archived: Optional[bool] = Query(None, description="Filter by archived status"),
order_by: str = Query("updated desc", description="Order by field and direction"),
):
"""Get all notebooks with optional filtering and ordering."""
try:
# Validate order_by against allowlist to prevent SurrealQL injection
allowed_fields = {"name", "created", "updated"}
allowed_directions = {"asc", "desc"}
parts = order_by.strip().lower().split()
if len(parts) == 1:
if parts[0] not in allowed_fields:
raise HTTPException(
status_code=400,
detail=f"Invalid order_by field: '{order_by}'. Allowed fields: {', '.join(sorted(allowed_fields))}",
)
validated_order_by = parts[0]
elif len(parts) == 2:
if parts[0] not in allowed_fields or parts[1] not in allowed_directions:
raise HTTPException(
status_code=400,
detail=f"Invalid order_by: '{order_by}'. Allowed fields: {', '.join(sorted(allowed_fields))}. Allowed directions: asc, desc",
)
validated_order_by = f"{parts[0]} {parts[1]}"
else:
raise HTTPException(
status_code=400,
detail=f"Invalid order_by format: '{order_by}'. Expected 'field' or 'field direction'",
)
# Build the query with countsView on GitHub (pinned to a7de90d38a)
Solutions
- Use only name, created, or updated as the field
- Optional direction is supported as second word: 'created desc'
- Map UI sort keys to these three fields in the client
Example fix
// before GET /api/v1/notebooks?order_by=title // after GET /api/v1/notebooks?order_by=name desc
Defensive patterns
Strategy: validation
Validate before calling
const FIELDS = ['name','created','updated'];
const parts = orderBy.trim().toLowerCase().split(/\s+/);
if (!FIELDS.includes(parts[0])) throw new Error(`order_by field must be one of ${FIELDS.join(', ')}`); Type guard
const isValidOrderBy = (s: string) => /^(name|created|updated)( (asc|desc))?$/.test(s.trim().toLowerCase());
Try / catch
try { await api.getNotebooks({order_by: q}); } catch (e) { if (e.status === 400 && /order_by/.test(e.detail)) refetchSorted('created'); } Prevention
- Constrain sort dropdowns to the three allowed fields
- Validate order_by client-side with a regex
- Fall back to default sort on 400
When it happens
Trigger: GET /notebooks?order_by=title, ?order_by=Name (actually works because of .lower(), but 'title', 'id', 'notebook' fail), or any single word not in the allow-list.
Common situations: Frontend sort dropdown using a column key that isn't one of the three allowed fields; copied API examples with wrong field names.
Related errors
- Invalid order_by: '{order_by}'. Allowed fields: {', '.join(s
- No embedding model configured. Please configure one in the M
- Item type must be either 'source' or 'note'
- Invalid model type. Must be one of: {valid_types}
- {field} is required and cannot be cleared, only reassigned
AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27).
Data as JSON: /api/errors/625de4d0bbf5ea38.
Report an issue: GitHub.