TencentCloud/TencentDB-Agent-Memory · error
Invalid scoped storage key: ${JSON.stringify(key)}
Error message
Invalid scoped storage key: ${JSON.stringify(key)} What it means
ScopedStorageAdapter.key() rejects keys that are not plain strings, contain a NUL byte, or start with '/' or '\'. The adapter namespaces all keys under a prefix, so raw absolute-looking or malformed keys would break the scoping contract; it throws rather than silently normalizing them.
Source
Thrown at MemoryCore/src/core/storage/adapter.ts:28
* Eventually, callers may inline IStorageBackend calls directly and
* this adapter can be removed.
*/
import type { IStorageBackend, StorageObject, ListEntry, ListObjectsOptions, ListResult, PutObjectOptions } from "./types.js";
class ScopedStorageBackend implements IStorageBackend {
readonly type: "local" | "cos";
private readonly prefix: string;
constructor(private readonly base: IStorageBackend, prefix: string) {
this.type = base.type;
const normalized = prefix.replace(/^\/+/, "").replace(/\/+/g, "/");
this.prefix = normalized && !normalized.endsWith("/") ? `${normalized}/` : normalized;
}
private key(key: string): string {
if (typeof key !== "string" || key.includes("\0") || key.startsWith("/") || key.startsWith("\\")) {
throw new Error(`Invalid scoped storage key: ${JSON.stringify(key)}`);
}
const normalized = key.replace(/^\/+/, "").replace(/\\+/g, "/").replace(/\/+/g, "/");
if (normalized.split("/").some((part) => part === "..")) {
throw new Error(`Path traversal rejected in scoped storage key: ${key}`);
}
return `${this.prefix}${normalized}`;
}
private unkey(key: string): string {
return key.startsWith(this.prefix) ? key.slice(this.prefix.length) : key;
}
async putObject(key: string, content: string | Buffer, opts?: PutObjectOptions): Promise<void> {
return this.base.putObject(this.key(key), content, opts);
}
async appendObject(key: string, content: string | Buffer): Promise<void> {
return this.base.appendObject(this.key(key), content);View on GitHub (pinned to 3efcd317b8)
Solutions
- Pass a relative, prefix-free key such as 'persona/main.md' instead of '/persona/main.md'
- Convert Windows backslash separators to forward slashes before calling the adapter
- Sanitize or strip NUL/control characters from keys derived from user input
- Ensure the key argument is always a non-null string (validate at the caller boundary)
Example fix
// before
await adapter.putObject('/users/' + name, buf);
// after
const rel = String(name).replace(/^[\\/]+/, '');
await adapter.putObject(`users/${rel}`, buf); Defensive patterns
Strategy: validation
Validate before calling
function assertSafeKey(key) {
if (typeof key !== 'string' || key.length === 0) throw new TypeError('key must be a non-empty string');
if (key.includes('\0')) throw new Error('key contains NUL byte');
if (key.startsWith('/') || key.startsWith('\\')) throw new Error(`key must be relative: ${key}`);
} Type guard
const isValidKey = (k) => typeof k === 'string' && k.length > 0 && !k.includes('\0') && !k.startsWith('/') && !k.startsWith('\\'); Try / catch
try {
await adapter.putObject(key, buf);
} catch (e) {
if (String(e.message).startsWith('Invalid scoped storage key')) {
throw new BadRequestError(`invalid storage key: ${key}`);
}
throw e;
} Prevention
- Centralize key construction in one helper that enforces relative, forward-slash keys
- Never concatenate absolute paths or user-supplied path prefixes into keys
- Normalize backslashes to slashes at the system boundary (Windows callers)
- Add unit tests asserting adapter rejects '/', '\\' and NUL-containing keys
When it happens
Trigger: Calling putObject, appendObject, obj, exists, result, or deleteObject with a key that is not a string, contains '\0', or starts with '/' or '\'.
Common situations: Concatenating a path-like base ('/data/') with the key; Windows-style '\dir\file' paths; null/undefined keys from broken callers; keys containing binary junk from decoding errors.
Related errors
- Generation log object key exceeds COS limit
- Invalid generation log key
- Invalid generation log cursor
- Path traversal rejected in scoped storage key: ${key}
- Storage key must be relative, got absolute: ${key}
AI-assisted analysis of TencentCloud/TencentDB-Agent-Memory@3efcd317b8 (2026-09-01).
Data as JSON: /api/errors/2c87dd356a1e49af.
Report an issue: GitHub.