{"record":{"id":"30004745bcd636d2","repo":"Mintplex-Labs/anything-llm","slug":"key-must-be-a-string","errorCode":null,"errorMessage":"Key must be a string","messagePattern":"Key must be a string","errorType":"validation","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"server/models/systemPromptVariables.js","lineNumber":358,"sourceCode":"          result = result.replace(match, variable.value || match);\n        }\n      }\n      return result;\n    } catch (error) {\n      console.error(\"Error in expandSystemPromptVariables:\", error);\n      return str;\n    }\n  },\n\n  /**\n   * Internal function to check if a variable key is valid\n   * @param {string} key\n   * @param {boolean} checkExisting\n   * @returns {Promise<boolean>}\n   */\n  _checkVariableKey: async function (key = null, checkExisting = true) {\n    if (!key) throw new Error(\"Key is required\");\n    if (typeof key !== \"string\") throw new Error(\"Key must be a string\");\n    if (!/^[a-zA-Z0-9_]+$/.test(key))\n      throw new Error(\"Key must contain only letters, numbers and underscores\");\n    if (key.length > 255)\n      throw new Error(\"Key must be less than 255 characters\");\n    if (key.length < 3) throw new Error(\"Key must be at least 3 characters\");\n    if (key.startsWith(\"user.\"))\n      throw new Error(\"Key cannot start with 'user.'\");\n    if (key.startsWith(\"system.\"))\n      throw new Error(\"Key cannot start with 'system.'\");\n    if (checkExisting && (await this.get(key)) !== null)\n      throw new Error(\"System prompt variable with this key already exists\");\n\n    return true;\n  },\n};\n\nmodule.exports = { SystemPromptVariables };\n","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/server/models/systemPromptVariables.js#L340-L376","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nSystemPromptVariables.create({ key: 42, value: 'x' }); // throws: Key must be a string\n\n// after\nSystemPromptVariables.create({ key: String(42), value: 'x' }); // '42' passes the format checks","handlingStrategy":"type-guard","validationCode":"if (typeof key !== 'string') {\n  return res.status(400).json({ error: 'key must be a string' });\n}","typeGuard":"/** @param {unknown} v */\nfunction isStringKey(v) {\n  return typeof v === 'string';\n}","tryCatchPattern":"try {\n  await SystemPromptVariables.create({ key, value });\n} catch (err) {\n  if (err.message === 'Key must be a string') {\n    return res.status(400).json({ error: 'Send key as a JSON string' });\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["system-prompt","validation","type-check"],"backgroundTag":"invalid-argument-value","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}