ZhuLinsen/daily_stock_analysis · error · RuntimeError
history deletion made no progress
Error message
history deletion made no progress
What it means
RuntimeError('history deletion made no progress') is an internal 500 raised inside DELETE /history/by-code/{stock_code}. The loop repeatedly fetches a batch of records matching the code candidates and deletes them by ID; if db_manager.delete_analysis_history_records(record_ids) returns 0 while the query still returns rows, the loop would spin forever, so the guard converts it into an error. It signals a mismatch between what get_analysis_history_paginated returns and what the delete call can actually remove.
Source
Thrown at api/v1/endpoints/history.py:335
if not candidates:
raise HTTPException(
status_code=400,
detail={"error": "invalid_request", "message": "stock_code 不能为空"},
)
deleted = 0
while True:
records, _ = db_manager.get_analysis_history_paginated(
code=candidates,
limit=_DELETE_BY_CODE_BATCH_SIZE,
)
record_ids = [r.id for r in records if r.id is not None]
if not record_ids:
break
batch_deleted = db_manager.delete_analysis_history_records(record_ids)
if batch_deleted == 0:
raise RuntimeError("history deletion made no progress")
deleted += batch_deleted
if len(records) < _DELETE_BY_CODE_BATCH_SIZE:
break
return DeleteHistoryResponse(deleted=deleted)
except HTTPException:
raise
except Exception as e:
logger.error(f"按股票代码删除历史记录失败: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail={"error": "internal_error", "message": f"删除失败: {str(e)}"},
)
@router.delete(
"",View on GitHub (pinned to 5159bd72e8)
Solutions
- Retry the DELETE request once — a transient race (another client deleted the batch between read and delete) resolves itself.
- Check server logs (the exception is logged with traceback via logger.error('按股票代码删除历史记录失败')) for the underlying DB error.
- Inspect src/repositories (delete_analysis_history_records) and confirm its deletion criteria match get_analysis_history_paginated's code filter.
- If concurrent deletions are expected in your workflow, serialize them (delete one stock at a time from one client).
Defensive patterns
Strategy: retry
Try / catch
try {
await delByCode(code);
} catch (e) {
if (isHttp500(e) && /no progress/.test(e.message)) {
await sleep(500); // concurrent delete race; retry once
await delByCode(code);
} else throw e;
} Prevention
- Avoid firing concurrent DELETE /by-code calls for the same stock from multiple clients.
- Treat 'made no progress' as a transient race first, a repository bug second (check logs).
- Monitor server logs for the 按股票代码删除历史记录失败 traceback when it repeats.
When it happens
Trigger: Database records whose id is non-None in the read path but fail to delete (row-level permissions, concurrent deletion by another request, a stale read replica serving reads while deletes go to primary), or a delete_analysis_history_records implementation that filters rows (e.g. soft-delete already applied) and reports 0 affected rows.
Common situations: Two concurrent DELETE /by-code calls racing on the same stock; SQLite/database locking causing the delete to silently affect 0 rows; a repository bug where the delete statement's WHERE clause uses a different code normalization than the paginated query
Related errors
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/840214e1b8a01522.
Report an issue: GitHub.