Sửa lỗi AWS Athena HIVE_CURSOR_ERROR: Row is not a valid JSON object

intermediate☁️ AWS2026-06-07| AWS Athena, Amazon S3, Hive JSON SerDe, OpenX JSON SerDe

Error Message

HIVE_CURSOR_ERROR: Row is not a valid JSON object
#aws-athena#s3#json#khac-phuc-su-co

Vấn đềBạn vừa mới nhấn 'Run Query' trên một câu lệnh SQL chuẩn, mong đợi nhận được một tập dữ liệu sạch từ S3. Thay vào đó, Athena dừng lại và đưa ra một lỗi khó chịu:

HIVE_CURSOR_ERROR: Row is not a valid JSON object

Lỗi này rất dễ gây nhầm lẫn. Bạn có thể mở tệp trong VS Code, thấy cú pháp được làm nổi bật hoàn hảo và tự hỏi điều gì đã sai. Vấn đề thường không nằm ở bản thân cú pháp JSON, mà ở cách dữ liệu được bố trí vật lý trên S3. Các trình phân tích cú pháp (parser) mặc định của Athena cực kỳ khắt khe về cấu trúc tệp.

Tóm tắt: Cách khắc phục nhanhAthena không đọc các tệp JSON tiêu chuẩn. Nó yêu cầu định dạng JSON Lines (ndjson). Nếu dữ liệu của bạn được định dạng pretty-printed (thụt lề trên nhiều dòng) hoặc được bọc trong một JSON array (sử dụng []), truy vấn sẽ thất bại ngay lập tức.

  • Làm phẳng các đối tượng: Mỗi bản ghi phải nằm trên đúng một dòng.- Bỏ các ký tự bao bọc: Loại bỏ các dấu ngoặc mở [ và đóng ].- Làm sạch các dấu phẩy: Loại bỏ các dấu phẩy thường dùng để phân tách các đối tượng trong một mảng tiêu chuẩn.### Chuyển đổi tệp ngay lập tức với jqNếu bạn đã cài đặt jq học, hãy sử dụng lệnh này để chuyển đổi một tệp pretty-printed sang định dạng nén mà Athena yêu cầu:
jq -c . input.json > output.jsonl

Tại sao điều này xảy raHive JSON SerDe (Serializer/Deserializer) mặc định là một trình đọc dạng luồng (streaming reader). Nó không tải toàn bộ tệp 500MB của bạn vào bộ nhớ để phân tích cú pháp như một tài liệu duy nhất. Thay vào đó, nó quét tệp theo từng dòng, coi mỗi ký tự xuống dòng là điểm bắt đầu của một bản ghi mới, độc lập.

Hãy xem xét đoạn mã phổ biến sau:

{
  "id": 101,
  "status": "active"
}

Athena đọc dòng đầu tiên: {. Vì một dấu mở ngoặc nhọn đơn lẻ không phải là một đối tượng JSON hoàn chỉnh, trình phân tích cú pháp sẽ báo lỗi và dừng truy vấn. Điều tương tự cũng xảy ra nếu tệp của bạn bắt đầu bằng [ để biểu thị một mảng.

Các tình huống phổ biến và cách khắc phục### 1. JSON ở định dạng Pretty-PrintedĐây là nguyên nhân phổ biến nhất. Trong khi việc thụt lề giúp con người dễ đọc log hơn, nó lại làm hỏng Athena. Bạn phải làm phẳng các đối tượng này thành các chuỗi trên một dòng duy nhất.

Sai:

{
    "user_id": 5001,
    "action": "click"
}
{
    "user_id": 5002,
    "action": "view"
}

Đúng (JSON Lines):

{"user_id": 5001, "action": "click"}
{"user_id": 5002, "action": "view"}

2. Một bản ghi bị lỗi cấu trúcTrong một tập dữ liệu với hàng triệu hàng, chỉ cần thiếu một dấu ngoặc kép hoặc một ký tự đặc biệt không được escape là có thể làm hỏng cả truy vấn. Tôi đã từng thấy các truy vấn 100GB thất bại ở mức 99% tiến trình chỉ vì một byte bị lỗi.

Mẹo nhỏ: Sử dụng JSON Formatter & Validator để kiểm tra các hàng nghi vấn. Tôi thường mở sẵn ToolCraft để nhanh chóng dán và xác thực các dòng bị lỗi; nó chạy cục bộ trong trình duyệt của bạn, vì vậy dữ liệu log nhạy cảm của bạn không bao giờ rời khỏi máy.

3. Xử lý lỗi mượt mà với OpenX SerDeNếu luồng dữ liệu của bạn không đồng nhất và bạn muốn bỏ qua một vài hàng lỗi thay vì làm hỏng toàn bộ truy vấn, hãy chuyển sang OpenX SerDe. Nó bao gồm một flag cụ thể để bỏ qua các lỗi.

Cập nhật câu lệnh CREATE TABLE của bạn như sau:

ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  'ignore.malformed.json' = 'true'
)

Lưu ý rằng mặc dù truy vấn hiện sẽ hoàn thành, bất kỳ hàng nào bị lỗi cấu trúc sẽ xuất hiện dưới dạng NULL trong kết quả của bạn.

Script Python để định dạng hàng loạtNếu bạn có hàng ngàn tệp trên S3 cần sửa, script Python này sẽ chuyển đổi các mảng JSON tiêu chuẩn sang định dạng phân tách theo dòng mà Athena yêu cầu:

import json

def repair_athena_json(input_file, output_file):
    with open(input_file, 'r') as f:
        data = json.load(f)
    
    with open(output_file, 'w') as f:
        # Đảm bảo chúng ta lặp qua các mảng hoặc xử lý các đối tượng đơn lẻ
        records = data if isinstance(data, list) else [data]
        for entry in records:
            f.write(json.dumps(entry) + '\n')

# Cách sử dụng
repair_athena_json('logs_june.json', 'logs_fixed.jsonl')

Danh sách kiểm tra cuối cùng- Chạy một truy vấn thử nghiệm: Sử dụng SELECT * FROM table LIMIT 10. Nếu một vài hàng đầu tiên được tải, định dạng cơ bản của bạn có khả năng đã đúng.- Kiểm tra các giá trị NULL: Nếu bạn đã sử dụng flag ignore.malformed.json, hãy chạy lệnh đếm để xem bạn đang mất bao nhiêu dữ liệu: ```

SELECT count(*) FROM your_table WHERE primary_id IS NULL; ```- Kiểm tra bảng mã (Encoding): Lưu các tệp của bạn ở định dạng UTF-8 không có Byte Order Mark (BOM). Các ký tự ẩn ở đầu tệp có thể kích hoạt lỗi HIVE_CURSOR_ERROR ngay lập tức.## Chiến lược phòng ngừa- Cấu hình bộ ghi log (Logger): Nếu bạn sử dụng Python, hãy chuyển sang python-json-logger. Đối với Node.js, hãy sử dụng pino. Cả hai đều hỗ trợ đầu ra ndjson theo mặc định.- Áp dụng Parquet: Đối với môi trường production, JSON rất tốn kém. Hãy chuyển đổi dữ liệu của bạn sang Parquet bằng AWS Glue. Parquet giúp truy vấn nhanh hơn, lưu trữ rẻ hơn và tránh hoàn toàn các lỗi phân tích cú pháp văn bản.

Related Error Notes