Mintplex-Labs/anything-llm · warning
Key must be a string
Error message
Key must be a string
What it means
The same key validator rejects any key whose typeof is not 'string' - numbers, arrays, and objects all fail. This guards both the Prisma write and the {{key}} template expansion from non-text keys before they reach the database.
Source
Thrown at server/models/systemPromptVariables.js:358
result = result.replace(match, variable.value || match);
}
}
return result;
} catch (error) {
console.error("Error in expandSystemPromptVariables:", error);
return str;
}
},
/**
* Internal function to check if a variable key is valid
* @param {string} key
* @param {boolean} checkExisting
* @returns {Promise<boolean>}
*/
_checkVariableKey: async function (key = null, checkExisting = true) {
if (!key) throw new Error("Key is required");
if (typeof key !== "string") throw new Error("Key must be a string");
if (!/^[a-zA-Z0-9_]+$/.test(key))
throw new Error("Key must contain only letters, numbers and underscores");
if (key.length > 255)
throw new Error("Key must be less than 255 characters");
if (key.length < 3) throw new Error("Key must be at least 3 characters");
if (key.startsWith("user."))
throw new Error("Key cannot start with 'user.'");
if (key.startsWith("system."))
throw new Error("Key cannot start with 'system.'");
if (checkExisting && (await this.get(key)) !== null)
throw new Error("System prompt variable with this key already exists");
return true;
},
};
module.exports = { SystemPromptVariables };
View on GitHub (pinned to 3aec848f28)
Solutions
- Send key as a JSON string ("key": "my_key", not 42)
- Coerce with String(key) only after confirming the value is scalar text
- Add schema validation (e.g. zod/yup) on the route so non-string keys are rejected at the boundary
Example fix
// before
SystemPromptVariables.create({ key: 42, value: 'x' }); // throws: Key must be a string
// after
SystemPromptVariables.create({ key: String(42), value: 'x' }); // '42' passes the format checks Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof key !== 'string') {
return res.status(400).json({ error: 'key must be a string' });
} Type guard
/** @param {unknown} v */
function isStringKey(v) {
return typeof v === 'string';
} Try / catch
try {
await SystemPromptVariables.create({ key, value });
} catch (err) {
if (err.message === 'Key must be a string') {
return res.status(400).json({ error: 'Send key as a JSON string' });
}
throw err;
} Prevention
- Type the request body with a schema validator (zod/yup) so non-string keys are rejected at the route
- Send identifiers as JSON strings, never bare numbers or arrays
- Coerce scalar inputs with String() at the boundary, never deep in the model
When it happens
Trigger: Passing key: 123 or key: ['a'] in the payload; a JSON body where the key field is a number literal ("key": 42); key built from un-serialized structured data.
Common situations: Client-side forms returning numbers for identifier-like fields; API integrations generated from typed schemas where key is typed as number; test fixtures reusing objects as keys.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Key is required
- Key must contain only letters, numbers and underscores
- Key must be less than 255 characters
- Key must be at least 3 characters
- Key cannot start with 'user.'
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/30004745bcd636d2.
Report an issue: GitHub.