iflytek/astron-agent · error · BusinessException

8515

8515

Error message

database.table.delete.failed

What it means

Thrown by DatabaseService.deleteTable when any unexpected exception occurs while deleting a table record and its field rows (the generic catch-all after BusinessException is rethrown). Code 8515, message 'database.table.delete.failed'. It wraps root causes like SQL errors, mapper failures, or constraint violations during the delete transaction.

Solutions

  1. Check the server log for 'Failed to delete table, tbId=...' to find the root-cause stack trace from the wrapped exception.
  2. Verify database connectivity and credentials (datasource health, connection pool exhaustion).
  3. Confirm the tbId exists and is not locked by a concurrent transaction; retry after the lock clears.
  4. Check for foreign-key constraints referencing the table rows that block the delete.
  5. Re-run the delete after fixing the root cause; if permission-related, a different BusinessException is raised instead.

Example fix

// before: only generic handling
try {
    removeTable(tbId);
} catch (Exception ex) {
    throw new BusinessException(ResponseEnum.DATABASE_TABLE_DELETE_FAILED);
}
// after: distinguish and surface root cause
try {
    removeTable(tbId);
} catch (BusinessException ex) {
    throw ex;
} catch (DuplicateKeyException | DataIntegrityViolationException ex) {
    log.error("Table rows still referenced, tbId={}", tbId, ex);
    throw new BusinessException(ResponseEnum.DATABASE_TABLE_DELETE_FAILED);
}
Defensive patterns

Strategy: try-catch

Validate before calling

Long count = dbTableMapper.selectCount(new LambdaQueryWrapper<DbTable>().eq(DbTable::getTbId, tbId));
if (count == 0) throw new IllegalStateException("table does not exist: " + tbId);

Try / catch

try {
    databaseService.deleteTable(tbId);
} catch (BusinessException ex) {
    if ("database.table.delete.failed".equals(ex.getMessage())) {
        log.error("delete failed for tbId={}, root cause in server log", tbId, ex);
    }
    throw ex;
}

Prevention

When it happens

Trigger: Calling deleteTable(tbId) when the underlying DELETE via dbTableFieldMapper/dbTable mapper fails — e.g. DB connection failure, SQL syntax error, foreign-key constraint, or the row was concurrently deleted. BusinessException from permission checks is rethrown unchanged, so this error only appears for non-business exceptions.

Common situations: Database unreachable or credentials rotated; table row locked by a concurrent transaction; schema drift between the entity mapping and actual DB schema; disk full on the DB host.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/256a68a87ac54978. Report an issue: GitHub.

Appendix: source

Thrown at console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/database/DatabaseService.java:665

            dbTableDto.setName(dbTable.getName());
            String ddl = buildDDL(dbTableDto, DBOperateEnum.DELETE.getCode(), null);
            // Delete from core system
            for (String stmt : safeSplitStatements(ddl)) {
                SqlRenderer.denyMultiStmtOrComment(stmt); // At this point each statement does not contain semicolon
                coreSystemService.execDDL(stmt, UserInfoManagerHandler.getUserId(), SpaceInfoUtil.getSpaceId(), dbInfo.getDbId());
            }
            dbTableMapper.update(new UpdateWrapper<DbTable>().lambda()
                    .eq(DbTable::getId, tbId)
                    .set(DbTable::getDeleted, true));
            // Delete table fields
            dbTableFieldMapper.delete(new UpdateWrapper<DbTableField>().lambda()
                    .eq(DbTableField::getTbId, tbId));
        } catch (BusinessException ex) {
            log.error("Failed to delete table, tbId={}", tbId);
            throw ex;
        } catch (Exception ex) {
            log.error("Failed to delete table, tbId={}", tbId, ex);
            throw new BusinessException(ResponseEnum.DATABASE_TABLE_DELETE_FAILED);
        }
    }

    public void operateTableData(DbTableOperateDto dbTableOperateDto) {
        dataPermissionCheckTool.checkTbBelong(dbTableOperateDto.getTbId());
        try {
            DbTable dbTable = dbTableMapper.selectById(dbTableOperateDto.getTbId());
            List<DbTableField> fields = dbTableFieldMapper.selectList(new QueryWrapper<DbTableField>().lambda()
                    .eq(DbTableField::getTbId, dbTable.getId()));
            DbInfo dbInfo = dbInfoMapper.selectById(dbTable.getDbId());

            // Validate and execute one by one (can be batched to improve availability)
            final int BATCH = 100; // Adjustable
            List<DbTableDataDto> rows = dbTableOperateDto.getData();
            for (int i = 0; i < rows.size(); i++) {
                DbTableDataDto data = rows.get(i);
                validateParams(data.getTableData(), fields, data.getOperateType());

View on GitHub (pinned to 5e758547a8)