From c1ccfb397b158635d9f7dddb370ffed1ec49efc3 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 23 Apr 2026 22:47:23 -0700 Subject: [PATCH] fix(ui/memory): preserve slashes in key path encoding The backend route `/v1/memory/{key:path}` supports keys with slashes, but `encodeURIComponent` encoded `/` as `%2F`. Some proxies (nginx default, CloudFlare, AWS ALB) reject or re-decode `%2F` mid-flight, so UI update/delete calls on slash-containing keys could fail or silently misroute. New helper `encodeMemoryKeyForPath` splits by `/`, URL-encodes each segment, then rejoins with literal `/`. Every other unsafe char (spaces, `?`, `#`, `%`) stays encoded per-segment; slashes stay as path delimiters, matching what the `:path` converter expects. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/components/networking.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 3355079f7bc..44208904a70 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -9991,6 +9991,22 @@ export const listMCPUserCredentials = async ( // Memory management (/v1/memory) // ============================================================ +/** + * Encode a memory key for use in a URL path segment. + * + * The backend route is declared as `/v1/memory/{key:path}`, which supports + * slashes in the key (e.g. `user/123/notes`). Plain `encodeURIComponent` + * encodes `/` as `%2F`, and some proxies/middlewares (nginx default, + * CloudFlare, AWS ALB) either reject or silently re-decode `%2F`, which + * can break the request before FastAPI ever sees it. + * + * We keep slashes literal as path delimiters while still encoding every + * other potentially-unsafe character (spaces, `?`, `#`, `%`, etc.) per + * path segment. + */ +const encodeMemoryKeyForPath = (key: string): string => + key.split("/").map(encodeURIComponent).join("/"); + export interface MemoryRow { memory_id: string; key: string; @@ -10075,7 +10091,7 @@ export const updateMemory = async ( key: string, payload: { value?: string; metadata?: unknown }, ): Promise => { - const encoded = encodeURIComponent(key); + const encoded = encodeMemoryKeyForPath(key); const url = proxyBaseUrl ? `${proxyBaseUrl}/v1/memory/${encoded}` : `/v1/memory/${encoded}`; @@ -10098,7 +10114,7 @@ export const deleteMemory = async ( accessToken: string, key: string, ): Promise => { - const encoded = encodeURIComponent(key); + const encoded = encodeMemoryKeyForPath(key); const url = proxyBaseUrl ? `${proxyBaseUrl}/v1/memory/${encoded}` : `/v1/memory/${encoded}`;