API 错误 400 "Text Content Blocks Must Be Non-Empty":原因与 5 个修复方法 指的是 Anthropic Messages API 请求中至少包含一个 text 值为空的文本块。该请求在消息层面看起来可能是有效的,但服务提供方会在生成前拒绝它,因为文本内容块必须至少包含一个字符。
快速修复很简单:移除空文本块,在构建请求之前裁剪仅包含空白字符的用户输入,并且绝不要发送诸如 {"type":"text","text":""} 这样的占位块。更难的部分是找出这些块是在哪里引入的。在生产应用中,它们通常来自聊天 UI 草稿、空的检索片段、Markdown 清理器、模板变量、流式转写缓冲区,或是在知道是否存在文本之前就先构建内容数组的多模态适配器。
使用本指南来调试该错误、修补请求构建器,并添加一个预检保护,以免同样的 400 再次上线。
快速回答:API 错误 400 "Text Content Blocks Must Be Non-Empty"
Anthropic 接受消息 content 为普通字符串或带类型的内容块数组。在数组形式中,文本块如下所示:
{
"type": "text",
"text": "Summarize this support ticket."
}
这会失败,因为 text 字段为空:
{
"type": "text",
"text": ""
}
如果你的应用将仅包含空白字符的值规范化为空字符串,实际中这也会失败:
{
"type": "text",
"text": " "
}
最安全的规则是:
- 在构建 Anthropic 请求之前裁剪文本值。
- 丢弃裁剪后文本为空的文本块。
- 如果某条消息不再包含任何内容块,就不要发送该消息。
- 记录清理后的 payload 结构,但不要记录私密提示文本。
- 针对空字符串、空白字符、null 和空检索结果添加单元测试。
这就是针对 API 错误 400 "Text Content Blocks Must Be Non-Empty":原因与 5 个修复方法 的实用修复方案。
为什么会发生这个错误
Anthropic 的 Messages API 使用结构化的对话轮次。每个输入消息都有一个 role 和 content。content 的值可以是单个字符串,也可以是诸如文本块和图片块之类的块数组。官方 Messages API 参考文档将字符串内容描述为单个文本块的简写,并在文本块上将 text 列为 minLength: 1。
Anthropic 的错误参考将 HTTP 400 归类为 invalid_request_error:即请求格式或内容存在问题。因此,这不是速率限制、认证失败、服务提供方中断,也不是模型质量问题,而是一个请求验证问题。
对于 AI 产品团队来说,运维层面的教训很重要:重试同一个请求不会有帮助。你需要先修复 payload,再进行重试。
五个常见原因
1. 空的聊天输入被发送到 API
最常见的路径是聊天输入框允许用户提交空草稿,或者草稿在裁剪后变为空。
错误请求:
{
"model": "claude-sonnet-5",
"max_tokens": 512,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "" }
]
}
]
}
在 API 调用之前先修复它:
const input = userInput.trim();
if (!input) {
throw new Error("调用 Anthropic 之前消息文本是必填项。");
}
const messages = [
{
role: "user",
content: input
}
];
在 UI 中使用产品级的校验提示。不要让后端从供应商的 400 错误中才发现空提示词。
2. 检索加入了空块
RAG 流水线通常会把检索到的文档映射到提示词各部分。如果某个检索结果有空片段、被移除的 HTML 正文,或者失败的 OCR 字段,适配器就可能创建一个空的文本块。
有问题的适配器:
const content = retrievedDocs.map((doc) => ({
type: "text",
text: doc.cleanedText
}));
更安全的适配器:
const content = retrievedDocs
.map((doc) => (doc.cleanedText ?? "").trim())
.filter(Boolean)
.map((text) => ({ type: "text", text }));
如果模型需要源上下文,也要保留丢弃块的数量。如果所有检索到的块都是空的,就应停止并返回检索错误,而不是发送空提示词。
3. 模板变量渲染为空
提示词模板是导致 API 错误 400 "Text Content Blocks Must Be Non-Empty":原因与 5 个修复方法 的另一个常见原因。模板在代码里看起来是有内容的,但运行时可能渲染出一个空区域:
const prompt = `
Customer message:
${customerMessage}
`;
如果 customerMessage 是 undefined、null,或者清理后为空,那么最终提示词可能无效或为空。
使用显式必填字段:
function requiredText(name: string, value: unknown): string {
const text = String(value ?? "").trim();
if (!text) {
throw new Error(`缺少必填的提示词字段:${name}`);
}
return text;
}
const prompt = `Customer message:\n${requiredText("customerMessage", customerMessage)}`;
这会把模糊的供应商错误转化为带有缺失字段名称的本地应用错误。
4. 多模态构建器添加了一个占位文本块
构建图文流程的团队有时会用一个占位文本块来初始化内容数组,然后稍后再填充。如果文本是可选的,而最终没有文本传入,占位符就会一直保持为空。
有问题的模式:
[
{ "type": "text", "text": "" },
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "..."
}
}
]
更安全的模式:
const content: Array<Record<string, unknown>> = [];
const instruction = optionalInstruction.trim();
if (instruction) {
content.push({ type: "text", text: instruction });
}
content.push({
type: "image",
source: imageSource
});
仅在相应内容存在时才构建块。不要将空文本块用作分隔符。
5. 消息历史压缩留下空白轮次
长期运行的助手通常会压缩或总结之前的轮次。如果压缩步骤移除了消息正文,但在历史记录中保留了该轮次,那么你的请求可能会包含一条空白的 assistant 或 user 消息。
失败示例:
{
"role": "assistant",
"content": [
{ "type": "text", "text": "" }
]
}
在每次调用之前使用历史清理器:
type TextBlock = { type: "text"; text: string };
type Message = { role: "user" | "assistant"; content: string | TextBlock[] };
function sanitizeMessages(messages: Message[]): Message[] {
return messages.flatMap((message) => {
if (typeof message.content === "string") {
const text = message.content.trim();
return text ? [{ ...message, content: text }] : [];
}
const content = message.content
.map((block) => ({ ...block, text: block.text.trim() }))
.filter((block) => block.type !== "text" || block.text.length > 0);
return content.length ? [{ ...message, content }] : [];
});
}
然后在调用 API 之前断言至少保留了一条消息。
可复制的预检验证器
在最终网络边界附近使用请求预检验证器。即使上游 UI、模板、RAG 或 memory 模块漏掉了空块,它也能将其捕获。
type ContentBlock =
| { type: "text"; text?: unknown }
| { type: string; [key: string]: unknown };
type AnthropicMessage = {
role: "user" | "assistant";
content: string | ContentBlock[];
};
export function validateAnthropicMessages(messages: AnthropicMessage[]) {
const cleaned = messages.flatMap((message, messageIndex) => {
if (typeof message.content === "string") {
const text = message.content.trim();
return text ? [{ ...message, content: text }] : [];
}
const content = message.content.flatMap((block, blockIndex) => {
if (block.type !== "text") return [block];
const text = String(block.text ?? "").trim();
if (!text) {
console.warn("Dropped empty Anthropic text block", {
messageIndex,
blockIndex
});
return [];
}
return [{ ...block, text }];
});
return content.length ? [{ ...message, content }] : [];
});
if (!cleaned.length) {
throw new Error("Anthropic request has no non-empty message content.");
}
return cleaned;
}
这是一种有意采取保守策略的做法。它会移除空文本块,保留非文本块,移除空消息,并且如果没有可用的消息内容,则拒绝调用模型。
Python 版本
如果你的后端是 Python,请使用相同的边界检查:
def sanitize_anthropic_messages(messages):
cleaned_messages = []
for message_index, message in enumerate(messages):
content = message.get("content")
if isinstance(content, str):
text = content.strip()
if text:
cleaned_messages.append({**message, "content": text})
continue
if isinstance(content, list):
cleaned_blocks = []
for block_index, block in enumerate(content):
if block.get("type") != "text":
cleaned_blocks.append(block)
continue
text = str(block.get("text") or "").strip()
if text:
cleaned_blocks.append({**block, "text": text})
else:
print(
"Dropped empty Anthropic text block",
{"message_index": message_index, "block_index": block_index},
)
if cleaned_blocks:
cleaned_messages.append({**message, "content": cleaned_blocks})
if not cleaned_messages:
raise ValueError("Anthropic 请求没有非空消息内容。")
return cleaned_messages
保持日志元数据的结构化。除非你的隐私政策和调试流程明确允许,否则不要记录原始用户提示、客户文档或私有检索文本。
调试清单
当API 错误 400 "Text Content Blocks Must Be Non-Empty":原因与 5 个修复方法出现在生产环境中时,请按以下顺序排查:
| 检查项 | 需要检查的内容 | 修复方法 |
|---|---|---|
| UI 输入 | 空的或仅包含空白字符的用户草稿 | 在存在修剪后的文本之前阻止提交 |
| 提示模板 | 必需变量渲染为空 | 按名称验证必填字段 |
| RAG 片段 | 空的 cleanedText、OCR 结果或 markdown 正文 |
过滤片段,如果所有上下文都为空则失败 |
| 多模态请求 | 在图像/文件块之前的占位文本块 | 仅在有文本时推送文本块 |
| 历史压缩 | 摘要后空白的用户或助手轮次 | 清理最终消息列表 |
| 网络边界 | 最终有效负载仍包含 text: "" |
添加预检验证器和单元测试 |
最终的网络有效负载是事实来源。如果你的日志显示没有空文本块,请确认 SDK 在序列化过程中没有将 null、undefined 或空数组转换为文本块。
要添加的单元测试
至少为以下情况添加测试:
const cases = [
{ name: "纯空字符串", content: "" },
{ name: "纯空白字符串", content: " " },
{ name: "空文本块", content: [{ type: "text", text: "" }] },
{ name: "缺少 text 字段", content: [{ type: "text" }] },
{ name: "text 字段为 null", content: [{ type: "text", text: null }] },
{ name: "有效文本块", content: [{ type: "text", text: "Hello" }] }
];
你的期望行为应该明确:
- 仅包含文本的空消息会在本地被移除或拒绝。
- 有效文本在去除首尾空白后仍然保留。
- 非文本内容块会被保留。
- 没有可用内容的请求会在调用 Anthropic 之前抛出错误。
- 抛出的错误应标识你的应用边界,而不只是提供商的响应。
Flatkey 的作用
如果你的团队通过 Flatkey 路由 Claude 流量,请保持相同的 Anthropic 负载规范。Flatkey 的 Anthropic SDK 指南在 Anthropic SDK 路径中使用 base_url="https://router.flatkey.ai",而 OpenAI 兼容 API 则对 chat-completions 风格请求使用 https://router.flatkey.ai/v1。请使用与你的客户端和端点形式相匹配的路由。
对于这个错误,Flatkey 最适合作为修复周围的操作层:
- 保留一个地方来验证请求是否到达网关。
- 在成功重试后比较请求状态和使用情况证据。
- 将一个小型冒烟测试与用户的完整提示分开。
- 避免在同一个适配器中混用 Anthropic 格式请求和 OpenAI 兼容请求。
如果你正在为 Claude 工作负载选择路由,请阅读 Claude API Proxy vs Multi-Model Router。如果你正在标准化工程师如何进行第一次安全调用,请将 Flatkey API quickstart 放在附近。对于更广泛的生产检查,请将其与 AI routing API metrics 以及 AI model catalog guide 搭配使用。
不要这样做
不要用盲目重试来解决 API 错误 400 "Text Content Blocks Must Be Non-Empty":原因与 5 个修复方法。提供商是在告诉你请求格式有误。
避免以下反模式:
| 反模式 | 为什么会失败 |
|---|---|
| 重试相同的负载 | 确定性的校验错误会一直失败 |
用 "." 替换空文本 |
这会掩盖上游数据丢失,并且可能改变模型行为 |
| 发送空的 assistant 回合 | 它会污染历史记录,并可能破坏响应续接 |
| 为了调试而记录完整提示词 | 它可能暴露客户数据或密钥 |
| 只修复 UI | 后端任务、RAG、webhook 和 agent 循环仍然可能创建空块 |
持久的修复方式是在生产者边界和最终 API 边界同时验证内容。
常见问题
这是 Anthropic 方面的故障吗?
不是。空文本块导致的 400 invalid_request_error 是请求校验问题。请检查您的应用发送的负载。
我可以发送普通字符串,而不是文本块数组吗?
可以。Anthropic 的 Messages API 允许消息 content 为字符串,文档将其描述为一个文本块的简写。当您只需要简单文本时,请使用字符串;当您需要多个块或多模态输入时,请使用数组。
空白字符是否应视为非空?
请在您自己的验证器中将仅包含空白字符的文本视为空。即使某个提供方接受了它,它也不是有用的提示内容,通常还表明存在 UI、模板或检索方面的 bug。
仅包含图片的消息可以吗?
多模态请求不需要空的文本占位符。如果您包含图片块,请直接构建图片块;只有在您确实有指令文本时,才添加文本块。
我应该记录什么?
记录消息数量、内容块类型、块索引、模型、端点、路由、状态码,以及可用时的请求 ID。除非您团队的隐私规则允许,否则避免记录完整的提示文本。
官方参考资料
- Anthropic Messages API 参考:https://platform.claude.com/docs/en/api/messages
- Anthropic API 错误参考:https://platform.claude.com/docs/en/api/errors
- Anthropic Messages API 指南:https://platform.claude.com/docs/en/build-with-claude/working-with-messages
- Flatkey Anthropic SDK 指南:https://docs.flatkey.ai/guides/anthropic-sdk.md
- Flatkey API 概览:https://docs.flatkey.ai/api-reference/overview.md



