报错是这么冒出来的。把线上请求日志直接导出成 jsonl 上传跑批,batch 创建后马上 failed,error_file 每一行都写 Missing required property: custom_id,而文件开头几行看着就是标准的 chat 请求体。
中间走过弯路,把「custom_id 必须唯一且不超过 64 个字符」当成主因,改完现象没变。
排查方向其实有限,先把可能性排一排。batch 每行要包成 custom_id、method、url、body 四段的外层结构。另一种情况是url 只能写 /v1/chat/completions 这类相对路径,带域名会判非法。
少数情况下是文件带 BOM 时首行解析失败,报错却指向下一行的字段。
第一件事是读 error_file 的第一条真实报错
curl -s https://api.openai.com/v1/files/$ERR/content -H "Authorization: Bearer $OPENAI_API_KEY" | head -2
随后把一条原始请求包成合规结构
python -c "import json;print(json.dumps({'custom_id':'r1','method':'POST','url':'/v1/chat/completions','body':{'model':'gpt-4o-mini','messages':[{'role':'user','content':'hi'}]}},ensure_ascii=False))"
接着统一存成不带 BOM 的 utf-8 文件
python -c "open('batch.jsonl','w',encoding='utf-8').write(open('raw.jsonl',encoding='utf-8-sig').readline())"
以 batch 用途上传并创建任务
curl https://api.openai.com/v1/files -H "Authorization: Bearer $OPENAI_API_KEY" -F purpose=batch -F file=@batch.jsonl
别只看报错消失,验证要多做一步。新的 batch 创建后 status 变成 validating 再进入 in_progress,error_file 为空,request_counts.total 与文件行数一致。
提醒一点。batch 文件不是原始请求日志,外层那四个字段缺一不可,直接改扩展名上传是最常见的失败原因。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。