{"record":{"id":"4fa4f9487f1be697","repo":"Budibase/budibase","slug":"ai-system-message-must-be-a-string","errorCode":null,"errorMessage":"AI system message must be a string","messagePattern":"AI system message must be a string","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/ai/js.ts","lineNumber":22,"sourceCode":"import { ai } from \"@budibase/pro\"\nimport sdk from \"../../../sdk\"\n\nconst MARKDOWN_CODE_BLOCK = /```(?:\\w+)?\\n([\\s\\S]+?)\\n```/\n\nexport async function generateJs(\n  ctx: UserCtx<GenerateJsRequest, GenerateJsResponse>\n) {\n  await context.ensureSnippetContext()\n  const currentContext = context.getCurrentContext()\n  const snippets = currentContext?.snippets || []\n  const { prompt, bindings = [] } = ctx.request.body\n\n  const request = ai.generateJs(bindings, snippets)\n  const systemMessage = request.messages.find(\n    message => message.role === \"system\"\n  )?.content\n  if (typeof systemMessage !== \"string\") {\n    throw new Error(\"AI system message must be a string\")\n  }\n  const { chat, providerOptions } = await sdk.ai.llm.getDefaultLLMOrThrow()\n  const result = await generateText({\n    model: chat,\n    instructions: systemMessage,\n    prompt,\n    providerOptions: providerOptions?.(false),\n  })\n\n  let code = result.text || \"\"\n  const match = code.match(MARKDOWN_CODE_BLOCK)\n  if (match) {\n    code = match[1]\n  }\n  ctx.body = { code }\n}\n","sourceCodeStart":4,"sourceCodeEnd":39,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/js.ts#L4-L39","documentation":"generateJs builds an AI request via ai.generateJs and requires the resulting message list to contain a system message whose content is a plain string. If no system-role message exists or its content is not a string (e.g. array of content parts), this Error is thrown before any LLM call is made.","triggerScenarios":"Calling generateJs where ai.generateJs returns messages with no role==='system' entry, or where the system message content is an array/prompt-parts object rather than a string (provider/SDK version drift).","commonSituations":"Upgrading the AI SDK changed system message serialization to content-part arrays; a custom snippets/bindings path suppresses the system message; tests stub ai.generateJs with incomplete messages.","solutions":["Update the ai.generateJs helper (or stub) to return a system message with string content.","If the content is an array of parts, join the text parts before passing: map(part => part.text).join('') or use the SDK's convertToCoreMessages-style helper.","Pin/align the AI SDK version so generateJs output matches the expected message shape."],"exampleFix":"// before\nconst systemMessage = request.messages.find(m => m.role === 'system')?.content\n// after\nconst rawContent = request.messages.find(m => m.role === 'system')?.content\nconst systemMessage = typeof rawContent === 'string' ? rawContent : Array.isArray(rawContent) ? rawContent.map(p => p.text).join('') : undefined","handlingStrategy":"type-guard","validationCode":"const msgs = ai.generateJs(bindings, snippets).messages\nconst sys = msgs.find(m => m.role === 'system')\nif (!sys || typeof sys.content !== 'string') throw new Error('generateJs must return a string system message')","typeGuard":"function hasStringSystemMessage(messages: { role: string; content: unknown }[]): messages is { role: 'system'; content: string }[] {\n  const sys = messages.find(m => m.role === 'system')\n  return typeof sys?.content === 'string'\n}","tryCatchPattern":"try {\n  const result = await generateJs(bindings, snippets, prompt)\n} catch (e) {\n  if (e.message === 'AI system message must be a string') {\n    // fall back to a locally-constructed system prompt string\n  } else throw e\n}","preventionTips":["Pin the AI SDK version; check release notes for message content shape changes.","Unit-test generateJs output shape, not just the LLM call.","When stubbing ai.generateJs in tests, include a system message with string content."],"tags":["ai","typescript","llm","message-format"],"backgroundTag":"schema-validation-failed","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}