Reliability and Routing22 tháng 9, 2026Flatkey Team

Lỗi API 400 "Text Content Blocks Must Be Non-Empty": Nguyên nhân và 5 cách khắc phục

Khắc phục lỗi API 400 "Text Content Blocks Must Be Non-Empty" trong các yêu cầu Anthropic bằng cách kiểm tra payload, mã sanitizer, ví dụ SDK và các bước QA.

Lỗi API 400 "Text Content Blocks Must Be Non-Empty": Nguyên nhân và 5 cách khắc phục

Lỗi API 400 "Text Content Blocks Must Be Non-Empty": Nguyên nhân và 5 cách khắc phục có nghĩa là một yêu cầu Anthropic Messages API chứa ít nhất một khối văn bản có giá trị text rỗng. Yêu cầu có vẻ hợp lệ ở cấp độ message, nhưng nhà cung cấp từ chối nó trước khi tạo sinh vì một khối nội dung văn bản phải chứa ít nhất một ký tự.

Cách khắc phục nhanh rất đơn giản: loại bỏ các khối văn bản rỗng, cắt bỏ đầu cuối các đầu vào người dùng chỉ gồm khoảng trắng trước khi xây dựng yêu cầu, và không bao giờ gửi các khối giữ chỗ như {"type":"text","text":""}. Phần khó hơn là tìm xem những khối đó được đưa vào từ đâu. Trong các ứng dụng production, chúng thường đến từ bản nháp giao diện chat, các đoạn truy xuất rỗng, trình làm sạch markdown, biến template, bộ đệm transcript streaming, hoặc các adapter đa phương thức xây dựng một mảng nội dung trước khi biết chắc có văn bản hay không.

Hãy dùng hướng dẫn này để gỡ lỗi lỗi, vá trình dựng yêu cầu, và thêm một lớp kiểm tra trước khi gửi để lỗi 400 này không xuất hiện lại.

Câu trả lời nhanh: Lỗi API 400 "Text Content Blocks Must Be Non-Empty"

Anthropic chấp nhận content của message dưới dạng chuỗi thuần hoặc một mảng các khối nội dung có kiểu. Ở dạng mảng, một khối văn bản trông như sau:

{
  "type": "text",
  "text": "Tóm tắt phiếu hỗ trợ này."
}

Khối này thất bại vì trường text rỗng:

{
  "type": "text",
  "text": ""
}

Điều này cũng có thể thất bại trong thực tế nếu ứng dụng của bạn chuẩn hóa một giá trị chỉ gồm khoảng trắng thành chuỗi rỗng:

{
  "type": "text",
  "text": "   "
}

Quy tắc an toàn nhất là:

  1. Cắt bỏ khoảng trắng ở các giá trị text trước khi xây dựng yêu cầu Anthropic.
  2. Loại bỏ các khối văn bản mà text sau khi trim là rỗng.
  3. Nếu một message không còn khối nội dung nào, đừng gửi message đó.
  4. Ghi log cấu trúc payload đã được làm sạch nhưng không ghi log nội dung prompt riêng tư.
  5. Thêm một unit test cho chuỗi rỗng, chuỗi chỉ có khoảng trắng, null, và kết quả truy xuất rỗng.

Đó là cách khắc phục thực tế cho Lỗi API 400 "Text Content Blocks Must Be Non-Empty": Nguyên nhân và 5 cách khắc phục.

Tại sao lỗi này xảy ra

Messages API của Anthropic sử dụng các lượt hội thoại có cấu trúc. Mỗi message đầu vào có một rolecontent. Giá trị content có thể là một chuỗi đơn, hoặc có thể là một mảng các khối như khối văn bản và khối hình ảnh. Tài liệu tham chiếu chính thức của Messages API mô tả nội dung dạng chuỗi là cách viết rút gọn cho một khối văn bản và liệt kê text trên một khối văn bản với minLength: 1.

Tài liệu lỗi của Anthropic phân loại HTTP 400 là invalid_request_error: một vấn đề với định dạng hoặc nội dung của yêu cầu. Vì vậy, đây không phải là lỗi giới hạn tần suất, lỗi xác thực, sự cố dịch vụ của nhà cung cấp, hay vấn đề chất lượng mô hình. Đây là vấn đề xác thực yêu cầu.

Đối với các nhóm sản phẩm AI, bài học vận hành rất quan trọng: thử lại cùng một yêu cầu sẽ không giúp ích gì. Bạn cần sửa payload trước khi thử lại.

Năm nguyên nhân phổ biến

1. Đầu vào Chat rỗng đi tới API

Con đường phổ biến nhất là một trình soạn chat cho phép người dùng gửi bản nháp trống hoặc một bản nháp trở nên trống sau khi cắt khoảng trắng.

Lỗi yêu cầu không hợp lệ:

{
  "model": "claude-sonnet-5",
  "max_tokens": 512,
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "" }
      ]
    }
  ]
}

Hãy sửa trước khi gọi API:

const input = userInput.trim();

if (!input) {
  throw new Error("Phải có nội dung tin nhắn trước khi gọi Anthropic.");
}

const messages = [
  {
    role: "user",
    content: input
  }
];

Sử dụng thông báo xác thực ở cấp sản phẩm trong giao diện. Đừng để backend phát hiện một prompt rỗng từ lỗi 400 của nhà cung cấp.

2. Truy xuất thêm các đoạn rỗng

Các pipeline RAG thường ánh xạ tài liệu được truy xuất vào các phần của prompt. Nếu một kết quả truy xuất có đoạn trích rỗng, phần thân HTML bị xóa, hoặc trường OCR bị lỗi, bộ điều hợp có thể tạo ra một khối văn bản rỗng.

Bộ điều hợp kém:

const content = retrievedDocs.map((doc) => ({
  type: "text",
  text: doc.cleanedText
}));

Bộ điều hợp an toàn hơn:

const content = retrievedDocs
  .map((doc) => (doc.cleanedText ?? "").trim())
  .filter(Boolean)
  .map((text) => ({ type: "text", text }));

Nếu mô hình cần ngữ cảnh nguồn, hãy giữ cả số lượng các đoạn đã bị loại bỏ. Nếu mọi đoạn được truy xuất đều rỗng, hãy dừng lại và trả về lỗi truy xuất thay vì gửi một prompt rỗng.

3. Biến template render ra không có gì

Template prompt cũng là một nguyên nhân thường gặp của Lỗi API 400 "Text Content Blocks Must Be Non-Empty": Nguyên nhân và 5 cách khắc phục. Một template có thể trông như đã có nội dung trong code nhưng lại render ra một phần rỗng khi chạy:

const prompt = `
Customer message:
${customerMessage}
`;

Nếu customerMessageundefined, null, hoặc trống sau khi làm sạch, prompt cuối cùng có thể vô dụng hoặc rỗng.

Hãy dùng các trường bắt buộc rõ ràng:

function requiredText(name: string, value: unknown): string {
  const text = String(value ?? "").trim();
  if (!text) {
    throw new Error(`Thiếu trường prompt bắt buộc: ${name}`);
  }
  return text;
}

const prompt = `Customer message:\n${requiredText("customerMessage", customerMessage)}`;

Cách này biến lỗi mơ hồ từ nhà cung cấp thành lỗi ứng dụng cục bộ với tên trường còn thiếu.

4. Trình tạo đa phương thức thêm một khối văn bản giữ chỗ

Các nhóm xây dựng luồng kết hợp hình ảnh và văn bản đôi khi khởi tạo mảng nội dung với một khối văn bản giữ chỗ rồi điền sau. Nếu văn bản là tùy chọn và không có văn bản nào đến, phần giữ chỗ sẽ vẫn rỗng.

Mẫu sai:

[
  { "type": "text", "text": "" },
  {
    "type": "image",
    "source": {
      "type": "base64",
      "media_type": "image/png",
      "data": "..."
    }
  }
]

Mẫu an toàn hơn:

const content: Array<Record<string, unknown>> = [];

const instruction = optionalInstruction.trim();
if (instruction) {
  content.push({ type: "text", text: instruction });
}

content.push({
  type: "image",
  source: imageSource
});

Xây dựng các khối chỉ khi nội dung tương ứng tồn tại. Không sử dụng các khối văn bản rỗng làm phần phân tách.

5. Việc nén lịch sử tin nhắn để lại các lượt trống

Các trợ lý chạy lâu thường nén hoặc tóm tắt các lượt trước đó. Nếu bước nén xóa phần thân của một tin nhắn nhưng vẫn giữ lượt đó trong lịch sử, yêu cầu của bạn có thể chứa một tin nhắn assistant hoặc user trống.

Ví dụ lỗi:

{
  "role": "assistant",
  "content": [
    { "type": "text", "text": "" }
  ]
}

Sử dụng bộ làm sạch lịch sử trước mỗi lần gọi:

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 }] : [];
  });
}

Sau đó xác nhận rằng còn ít nhất một tin nhắn trước khi gọi API.

Trình xác thực tiền kiểm có thể sao chép

Sử dụng trình xác thực tiền kiểm yêu cầu gần ranh giới mạng cuối cùng. Điều này phát hiện các khối trống ngay cả khi giao diện người dùng, mẫu, RAG hoặc mô-đun bộ nhớ ở phía trước bỏ sót chúng.

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("Đã loại bỏ khối văn bản Anthropic rỗng", {
          messageIndex,
          blockIndex
        });
        return [];
      }

      return [{ ...block, text }];
    });

    return content.length ? [{ ...message, content }] : [];
  });

  if (!cleaned.length) {
    throw new Error("Yêu cầu Anthropic không có nội dung tin nhắn không rỗng.");
  }

  return cleaned;
}

Điều này có chủ ý là bảo thủ. Nó loại bỏ các khối văn bản trống, giữ lại các khối không phải văn bản, xóa các tin nhắn trống và từ chối gọi mô hình nếu không còn nội dung tin nhắn nào có thể sử dụng.

Phiên bản Python

Nếu backend của bạn là Python, hãy sử dụng cùng một kiểm tra ở ranh giới:

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(
                        "Đã loại bỏ khối văn bản Anthropic trống",
                        {"message_index": message_index, "block_index": block_index},
                    )

            if cleaned_blocks:
                cleaned_messages.append({**message, "content": cleaned_blocks})

    if not cleaned_messages:
        raise ValueError("Yêu cầu Anthropic không có nội dung tin nhắn không trống.")

    return cleaned_messages

Giữ nguyên cấu trúc siêu dữ liệu log. Không ghi log nguyên văn prompt của người dùng, tài liệu khách hàng, hoặc văn bản truy xuất riêng tư trừ khi chính sách quyền riêng tư và quy trình gỡ lỗi của bạn cho phép rõ ràng.

Danh sách kiểm tra gỡ lỗi

Khi Lỗi API 400 "Text Content Blocks Must Be Non-Empty": Nguyên nhân và 5 cách khắc phục xuất hiện trong môi trường production, hãy gỡ lỗi theo thứ tự sau:

Kiểm tra Cần xem xét gì Cách khắc phục
Đầu vào UI Bản nháp người dùng trống hoặc chỉ có khoảng trắng Chặn gửi cho đến khi có văn bản đã được trim
Mẫu prompt Biến bắt buộc được render thành rỗng Xác thực các trường bắt buộc theo tên
Các chunk RAG cleanedText rỗng, kết quả OCR, hoặc nội dung markdown Lọc các chunk và báo lỗi nếu toàn bộ ngữ cảnh đều trống
Yêu cầu đa phương thức Khối văn bản giữ chỗ trước khối hình ảnh/tệp Chỉ đẩy các khối văn bản khi có văn bản
Nén lịch sử Người dùng hoặc trợ lý có lượt trao đổi trống sau khi tóm tắt Làm sạch danh sách tin nhắn cuối cùng
Ranh giới mạng Payload cuối cùng vẫn chứa text: "" Thêm bộ kiểm tra preflight và các bài kiểm thử đơn vị

Payload mạng cuối cùng là nguồn thông tin chuẩn xác. Nếu log của bạn không hiển thị khối văn bản trống nào, hãy xác nhận rằng SDK không chuyển đổi null, undefined, hoặc một mảng rỗng thành khối văn bản trong quá trình tuần tự hóa.

Các bài kiểm thử đơn vị cần thêm

Tối thiểu, hãy thêm các bài kiểm thử cho những trường hợp sau:

const cases = [
  { name: "chuỗi rỗng thuần túy", content: "" },
  { name: "chuỗi chỉ có khoảng trắng thuần túy", content: "   " },
  { name: "khối văn bản rỗng", content: [{ type: "text", text: "" }] },
  { name: "thiếu trường text", content: [{ type: "text" }] },
  { name: "trường text null", content: [{ type: "text", text: null }] },
  { name: "khối văn bản hợp lệ", content: [{ type: "text", text: "Hello" }] }
];

Hành vi mong đợi của bạn phải được nêu rõ ràng:

  • Các tin nhắn chỉ có văn bản rỗng sẽ bị loại bỏ hoặc bị từ chối cục bộ.
  • Văn bản hợp lệ vẫn được giữ lại sau khi loại bỏ khoảng trắng ở đầu và cuối.
  • Các khối nội dung không phải văn bản được giữ nguyên.
  • Một yêu cầu không có nội dung dùng được sẽ phát sinh lỗi trước khi gọi Anthropic.
  • Lỗi được ném ra phải xác định ranh giới ứng dụng của bạn, không chỉ phản hồi từ nhà cung cấp.

Flatkey phù hợp ở đâu

Nếu nhóm của bạn định tuyến lưu lượng Claude qua Flatkey, hãy giữ cùng một kỷ luật về payload của Anthropic. Hướng dẫn Anthropic SDK của Flatkey cho thấy base_url="https://router.flatkey.ai" cho đường dẫn Anthropic SDK, trong khi API tương thích OpenAI dùng https://router.flatkey.ai/v1 cho các yêu cầu kiểu chat-completions. Hãy dùng tuyến phù hợp với client và dạng endpoint của bạn.

Với lỗi này, Flatkey hữu ích nhất như lớp vận hành bao quanh bản sửa:

  • Giữ một nơi để xác minh liệu yêu cầu đã đến gateway hay chưa.
  • So sánh trạng thái yêu cầu và bằng chứng sử dụng sau một lần thử lại thành công.
  • Giữ một bài kiểm tra nhanh nhỏ tách biệt với prompt đầy đủ của người dùng.
  • Tránh trộn các yêu cầu định dạng Anthropic và các yêu cầu tương thích OpenAI trong cùng một adapter.

Nếu bạn đang chọn tuyến cho khối lượng công việc Claude, hãy đọc Claude API Proxy vs Multi-Model Router. Nếu bạn đang chuẩn hóa cách các kỹ sư thực hiện lần gọi an toàn đầu tiên, hãy để gần đó Flatkey API quickstart. Với các kiểm tra sản xuất rộng hơn, hãy kết hợp nội dung này với chỉ số API định tuyến AIhướng dẫn danh mục mô hình AI.

Không nên làm gì

Đừng giải quyết Lỗi API 400 "Text Content Blocks Must Be Non-Empty": Nguyên nhân và 5 cách khắc phục bằng cách thử lại mù quáng. Nhà cung cấp đang báo cho bạn rằng yêu cầu bị định dạng sai.

Tránh các phản mẫu sau:

Phản mẫu Vì sao thất bại
Thử lại cùng một payload Một lỗi xác thực tất định sẽ tiếp tục thất bại
Thay thế văn bản rỗng bằng "." Nó che giấu việc mất dữ liệu ở upstream và có thể thay đổi hành vi của mô hình
Gửi các lượt assistant rỗng Nó làm ô nhiễm lịch sử và có thể làm hỏng việc tiếp tục phản hồi
Ghi log toàn bộ prompt để gỡ lỗi Nó có thể làm lộ dữ liệu khách hàng hoặc bí mật
Chỉ sửa giao diện người dùng Các job backend, RAG, webhook và vòng lặp agent vẫn có thể tạo ra các khối rỗng

Bản sửa bền vững là xác thực nội dung ở cả ranh giới của bên tạo và ranh giới API cuối cùng.

Câu hỏi thường gặp

Đây có phải là sự cố ngừng dịch vụ của Anthropic không?

Không. Lỗi 400 invalid_request_error đối với các khối văn bản trống là vấn đề xác thực request. Hãy kiểm tra payload mà ứng dụng của bạn gửi đi.

Tôi có thể gửi một chuỗi văn bản thuần thay vì một mảng text block không?

Có. Messages API của Anthropic cho phép content của message là một chuỗi, và tài liệu mô tả đó là cách viết rút gọn cho một text block. Hãy dùng chuỗi khi bạn chỉ cần văn bản đơn giản. Hãy dùng mảng khi bạn cần nhiều khối hoặc đầu vào đa phương thức.

Có nên coi khoảng trắng là không rỗng không?

Hãy coi văn bản chỉ chứa khoảng trắng là rỗng trong trình kiểm tra của riêng bạn. Ngay cả khi một nhà cung cấp chấp nhận nó, đó cũng không phải là nội dung prompt hữu ích và thường cho thấy lỗi ở giao diện người dùng, template hoặc truy xuất dữ liệu.

Các message chỉ có hình ảnh có hoạt động không?

Một request đa phương thức không cần một placeholder văn bản trống. Nếu bạn bao gồm một image block, hãy tạo image block trực tiếp và chỉ thêm text block khi bạn có nội dung hướng dẫn thực sự.

Tôi nên ghi log những gì?

Ghi log số lượng message, loại content block, chỉ số block, model, endpoint, route, status code và request ID nếu có. Tránh ghi toàn bộ prompt text trừ khi quy định về quyền riêng tư của nhóm bạn cho phép.

Tài liệu tham khảo chính thức