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

  1. Retry the DELETE request once — a transient race (another client deleted the batch between read and delete) resolves itself.
  2. Check server logs (the exception is logged with traceback via logger.error('按股票代码删除历史记录失败')) for the underlying DB error.
  3. Inspect src/repositories (delete_analysis_history_records) and confirm its deletion criteria match get_analysis_history_paginated's code filter.
  4. 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

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.