Budibase/budibase · error · Error
AI user message must be a string
Error message
AI user message must be a string
What it means
generateCronExpression takes the prompt from the request body and passes it to ai.generateCronExpression, then locates the resulting user-role message to feed to the LLM via generateText. Because ModelMessage content can be an array of parts, the controller asserts it is a plain string before building the ModelMessage[]; if the produced user message content is not a string (e.g. undefined because no user message exists, or structured content), it throws this Error.
Source
Thrown at packages/server/src/api/controllers/ai/cron.ts:15
import { generateText, type ModelMessage } from "ai"
import { Ctx, GenerateCronRequest, GenerateCronResponse } from "@budibase/types"
import { ai } from "@budibase/pro"
import sdk from "../../../sdk"
export async function generateCronExpression(
ctx: Ctx<GenerateCronRequest, GenerateCronResponse>
) {
const { prompt } = ctx.request.body
const request = ai.generateCronExpression(prompt)
const userMessage = request.messages.find(
message => message.role === "user"
)?.content
if (typeof userMessage !== "string") {
throw new Error("AI user message must be a string")
}
const messages: ModelMessage[] = [{ role: "user", content: userMessage }]
const { chat, providerOptions } = await sdk.ai.llm.getDefaultLLMOrThrow()
const result = await generateText({
model: chat,
messages,
providerOptions: providerOptions?.(false),
})
const message = result.text?.trim()
if (message?.startsWith("Error generating cron:")) {
ctx.throw(400, message)
} else {
ctx.body = { message }
}
}View on GitHub (pinned to a81a902e9a)
Solutions
- Always send a non-empty string prompt in the request body: { prompt: "run every weekday at 9am" }.
- Validate prompt is present and a string client-side before calling the endpoint.
- If the prompt was provided and the error still occurs, inspect ai.generateCronExpression's output shape — content may be a content-parts array; flatten/normalize it to a string.
- Check for version drift between @budibase/types ModelMessage definitions and the controller's string assumption after upgrading.
Example fix
// before
const { prompt } = ctx.request.body // prompt undefined
const request = ai.generateCronExpression(prompt)
// after (server-side hardening)
if (typeof prompt !== "string" || !prompt.trim()) {
throw new HTTPError("Prompt is required", 400)
}
const request = ai.generateCronExpression(prompt) Defensive patterns
Strategy: validation
Validate before calling
if (typeof prompt !== "string" || !prompt.trim()) {
throw new Error("A non-empty prompt string is required")
}
await api.post("/api/ai/cron", { prompt }) Type guard
const isNonEmptyString = (v: unknown): v is string => typeof v === "string" && v.trim().length > 0
Try / catch
try {
const res = await api.post("/api/ai/cron", { prompt })
} catch (e) {
if (String(e.message).includes("AI user message must be a string")) {
// prompt missing or upstream message shape changed; resend with a string prompt or upgrade server
}
} Prevention
- Always send prompt as a non-empty string in the request body
- Validate payloads before calling AI endpoints
- Pin/align package versions so ModelMessage shapes match between packages
- Add a server-side early check that prompt is a string before calling ai.generateCronExpression
When it happens
Trigger: POST to the AI cron endpoint with body.prompt undefined/null/empty such that ai.generateCronExpression produces no string user message, or the returned message content is a non-string (array of content parts) shape.
Common situations: API clients calling the endpoint without a prompt field; automation/webhook callers sending an empty body; upstream ai.generateCronExpression shape changing to multi-part content so the find returns a message whose content is not a string.
Related errors
- Invalid interaction summary response
- Invalid agent request outcome response
- Error generating tables
- LLM not available
- AI message content must be a string
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/c3080f97f1972f81.
Report an issue: GitHub.