alibaba/page-agent · error · InvokeError
CONFIG_ERROR
CONFIG_ERROR
Error message
transformRequestBody failed: ${(error as Error).message} What it means
Thrown when the user-supplied transformRequestBody config callback raises an exception while transforming the outgoing request body. The library treats a failing request transformer as a configuration problem, not a runtime model failure. The original error is preserved as the cause.
Source
Thrown at packages/llms/src/OpenAIClient.ts:65
const requestBody: Record<string, unknown> = {
model: this.config.model,
messages,
tools: openaiTools,
parallel_tool_calls: false,
tool_choice: toolChoice,
}
// Only sent if the caller explicitly set it. Most new models throw if this is set.
if (this.config.temperature !== undefined) {
requestBody.temperature = this.config.temperature
}
modelPatch(requestBody, this.config.baseURL)
let transformedBody: Record<string, unknown> | undefined
try {
transformedBody = this.config.transformRequestBody(requestBody)
} catch (error) {
throw new InvokeError(
InvokeErrorTypes.CONFIG_ERROR,
`transformRequestBody failed: ${(error as Error).message}`,
error
)
}
const finalRequestBody = transformedBody ?? requestBody
// 2. Call API
let response: Response
try {
response = await this.fetch(`${this.config.baseURL}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),
},
body: JSON.stringify(finalRequestBody),
signal: abortSignal,View on GitHub (pinned to d02db1ee7c)
Solutions
- Inspect the preserved cause: catch (e) { e.cause } — the stack points at the exact line in your transformRequestBody that threw
- Make the transformer defensive: use optional chaining and only patch fields that exist
- Log or assert the requestBody shape once in dev to confirm the transformer's assumptions
- If no transformation is needed, omit transformRequestBody from the config entirely
Example fix
// before
new OpenAIClient({ ..., transformRequestBody: (body) => {
body.messages[0].content = body.messages[0].content.toUpperCase() // throws if content is undefined
}})
// after
new OpenAIClient({ ..., transformRequestBody: (body) => {
if (Array.isArray(body.messages) && typeof body.messages[0]?.content === 'string') {
body.messages[0].content = body.messages[0].content.toUpperCase()
}
return body
}}) Defensive patterns
Strategy: validation
Validate before calling
const body = { model: 'gpt-4o', messages: [{ role: 'user', content: 'hi' }] }
// dry-run the transformer before wiring it in
const out = config.transformRequestBody(body)
if (!out || typeof out !== 'object') throw new Error('transformRequestBody must return an object') Type guard
const isInvokeConfigError = (e: unknown): e is InvokeError => e instanceof InvokeError && e.code === 'CONFIG_ERROR'
Try / catch
try {
await client.invoke(req)
} catch (e) {
if (isInvokeConfigError(e)) {
console.error('Transformer bug:', (e as InvokeError).cause)
throw e // config bugs are deterministic — do not retry
}
throw e
} Prevention
- Write a unit test that runs transformRequestBody against a representative request body
- Use optional chaining and early returns inside transformers
- Never assume nested fields exist on the provider-shaped body
When it happens
Trigger: Setting config.transformRequestBody to a function that throws — e.g. it assumes a field exists on the body (requestBody.messages[0].content.replace(...)), mutates a frozen object, or contains a typo'd property access. It runs on every invoke() before the HTTP call, after modelPatch().
Common situations: A transformer written for one provider's schema (e.g. OpenAI 'messages') is used against a body shape another provider produces; transformer accesses undefined nested fields; transformer uses JSON.parse on an already-object value; SDK upgrade changes requestBody shape the transformer relied on.
Related errors
AI-assisted analysis of alibaba/page-agent@d02db1ee7c (2026-08-28).
Data as JSON: /api/errors/e398bde0068df818.
Report an issue: GitHub.