ruvnet/ruflo · error
namespace contains invalid characters (allowed…
Error message
namespace contains invalid characters (allowed: alphanumeric, _, -, ., :)
What it means
The final validateIdentifier check requires the whole namespace to match /^[a-zA-Z0-9_][a-zA-Z0-9_\-.:]{0,127}$/ — it must start with a letter, digit, or underscore, and contain only alphanumerics, _, -, ., : afterwards. memory_list throws 'namespace contains invalid characters (allowed: alphanumeric, _, -, ., :)' when the namespace survived the shell-meta and traversal checks but still contains an illegal character (space, /, +, unicode) or starts with '-', '.', or ':' (memory-tools.ts:705).
Solutions
- Use only [A-Za-z0-9_\-.:] and start with an alphanumeric or _ — e.g. 'my-patterns', 'team:blue'
- Slugify: ns.toLowerCase().replace(/[^a-z0-9_\-.:]+/g, '-').replace(/^[^a-z0-9_]+/, '')
- Map '/' hierarchies to ':' which the charset explicitly allows
- Add the identical regex test in your caller to fail fast with a localized error message
Example fix
// before
await mcp.callTool('memory_list', { namespace: 'team blue/2026' }); // space and slash -> invalid characters
// after
await mcp.callTool('memory_list', { namespace: 'team-blue:2026' }); Defensive patterns
Strategy: validation
Validate before calling
// Exact mirror of the library's identifier contract.
const IDENTIFIER_RE = /^[a-zA-Z0-9_][a-zA-Z0-9_\-.:]{0,127}$/;
function toValidNamespace(raw: string): string {
const slug = raw.replace(/[^A-Za-z0-9_\-.:]+/g, '-').replace(/^[^A-Za-z0-9_]+/, '');
if (!IDENTIFIER_RE.test(slug)) throw new RangeError(`cannot derive a valid namespace from: ${raw}`);
return slug;
}
// const ns = toValidNamespace(userInput); Type guard
function isValidNamespace(ns: string): ns is string {
return /^[a-zA-Z0-9_][a-zA-Z0-9_\-.:]{0,127}$/.test(ns);
} Try / catch
try {
await memoryList({ namespace: ns });
} catch (e) {
if (e instanceof Error && e.message.includes('namespace contains invalid characters')) {
// slugify and retry once; if still invalid, reject the input at your boundary
}
throw e;
} Prevention
- Adopt the exact charset [A-Za-z0-9_-.:] (first char alnum or _) as your namespace policy everywhere
- Slugify user-provided namespaces before any memory tool call
- Map URL/path hierarchies to ':' separators, not '/' or spaces
- Validate namespaces once at config/input load and reuse the validated value
When it happens
Trigger: memory_list with namespace 'my patterns' (space), 'team/blue' (slash), 'café' (unicode é), '+prod' or '-prod' or '.cache' (disallowed first character).
Common situations: Human-readable namespaces with spaces or slashes; URL- or path-derived namespaces; localized characters; the store/list asymmetry where memory_store accepted the same string because its write-side pattern allows spaces.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- namespace contains disallowed characters
- namespace exceeds 128 characters
- Namespace contains disallowed characters
- namespace contains path traversal
- namespace must be a non-empty string
AI-assisted analysis of ruvnet/ruflo@9c61c86f06 (2026-08-18).
Data as JSON: /api/errors/611233d470d5621a.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/memory-tools.ts:805
description: 'Enumerate stored memory entries (optionally filtered by namespace/tags) without semantic search. Use when native Glob is wrong because the entries are not files (they live in .swarm/memory.db). For inspection / audit / "what is in my memory" — pair with memory_search for retrieval-by-meaning.',
category: 'memory',
inputSchema: {
type: 'object',
properties: {
namespace: { type: 'string', description: 'Filter by namespace' },
limit: { type: 'number', description: 'Maximum results (default: 50)' },
offset: { type: 'number', description: 'Offset for pagination (default: 0)' },
},
},
handler: async (input) => {
await ensureInitialized();
const { listEntries } = await getMemoryFunctions();
const namespace = input.namespace as string | undefined;
const limit = (input.limit as number) || 50;
const offset = (input.offset as number) || 0;
if (namespace) { const vNs = validateIdentifier(namespace, 'namespace'); if (!vNs.valid) throw new Error(vNs.error); }
try {
const result = await listEntries({
namespace,
limit,
offset,
});
const entries = result.entries.map(e => ({
key: e.key,
namespace: e.namespace,
storedAt: e.createdAt,
updatedAt: e.updatedAt,
accessCount: e.accessCount,
hasEmbedding: e.hasEmbedding,
size: e.size,
}));
View on GitHub (pinned to 9c61c86f06)