AI classification fails when model returns empty content (JSONDecodeError) #106
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom
Intermittent AI classification failures where the model's HTTP request returns
200 OKbutjson.loads()raisesJSONDecodeError: Expecting value: line 1 column 1 (char 0)— i.e. themessage.contentfield is empty.Affected posts (from
packages/fenliu/fenliu-all.log):Root cause
AIClient.complete_jsondoes:For the failing posts the model returned
200 OKbutmessage.contentwas empty (char 0 = empty input), sojson.loads("")raised. The exception is caught inclassify_posts, which logs a warning andcontinues, leaving the post unclassified.Traceback from
logs/fenliu_debug.log:Possible causes (not confirmed)
The request sends
"thinking": {"type": "disabled"},"response_format": {"type": "json_object"}, andmax_tokens=300against Moonshotkimi-k2.6. Candidates to investigate:reasoning_contentand leavescontentempty (thinking toggle not honored).max_tokens=300truncates before any content is produced.The raw response body is not logged, so the exact cause can't be pinned down from current logs.
Fix direction
Make
complete_jsonrobust and observable:json.loads— on empty/unparseable content, fail closed (return an "unclassified" signal rather than raising).finish_reason,content,reasoning_content) at DEBUG when parsing fails, so the cause is visible next time.thinkingshould be enabled for kimi-k2.6 and the answer read fromreasoning_content, or whether empty-content responses should be retried.Impact
Affected posts are left
AI: pending(manual review). No mis-classification — just unclassified posts.Plan of attack:
packages/fenliu/src/fenliu/services/ai_classification.py—complete_jsonguards the wholechoices[0].message.contentextraction and parse; empty/missing/non-JSON content raises a new typedAIMalformedResponseError. Before raising it logs a WARNING withfinish_reason, the rawcontentrepr, and a boundedreasoning_contentexcerpt (~500 chars), so the candidate causes (thinking toggle ignored / content filter /max_tokenstruncation) are distinguishable on the next occurrence.packages/fenliu/src/fenliu/services/ai_pipeline.py— catches the typed exception before the genericexcept Exceptionand logs a terse one-liner with the post id, no traceback. The post staysAI: pendingfor manual review (deliberate: no retry, no fail-closed verdict — retry decision deferred until the logs reveal the actual cause).classify_postsleaves the post unclassified when a stage hits empty content.fix/issue-106-handle-empty-ai-content, single commit, PR tomain.