fix: return an explicit erro from the cron tool when scheduling a task fails instead of processing silently(#7513)

* fix: 定时任务创建失败时返回错误信息而非静默处理

* fix: test and format

---------

Co-authored-by: Soulter <905617992@qq.com>
This commit is contained in:
Hongbro886
2026-04-16 20:14:55 +08:00
committed by GitHub
parent ee85a4e50f
commit 22e8cbd10d
3 changed files with 40 additions and 22 deletions

View File

@@ -22,6 +22,12 @@ if TYPE_CHECKING:
from astrbot.core.star.context import Context
class CronJobSchedulingError(Exception):
"""Raised when a cron job fails to be scheduled."""
pass
class CronJobManager:
"""Central scheduler for BasicCronJob and ActiveAgentCronJob."""
@@ -59,7 +65,10 @@ class CronJobManager:
job.job_id,
)
continue
self._schedule_job(job)
try:
self._schedule_job(job)
except CronJobSchedulingError:
continue # Error already logged in _schedule_job
async def add_basic_job(
self,
@@ -181,8 +190,9 @@ class CronJobManager:
job.job_id, next_run_time=self._get_next_run_time(job.job_id)
)
)
except Exception as e:
logger.error(f"Failed to schedule cron job {job.job_id}: {e!s}")
except (ValueError, TypeError) as e:
logger.exception("Failed to schedule cron job %s", job.job_id)
raise CronJobSchedulingError(str(e)) from e
def _get_next_run_time(self, job_id: str):
aps_job = self.scheduler.get_job(job_id)

View File

@@ -7,6 +7,7 @@ from pydantic.dataclasses import dataclass
from astrbot.core.agent.run_context import ContextWrapper
from astrbot.core.agent.tool import FunctionTool, ToolExecResult
from astrbot.core.astr_agent_context import AstrAgentContext
from astrbot.core.cron.manager import CronJobSchedulingError
from astrbot.core.tools.registry import builtin_tool
_CRON_TOOL_CONFIG = {
@@ -112,14 +113,17 @@ class FutureTaskTool(FunctionTool[AstrAgentContext]):
"origin": "tool",
}
job = await cron_mgr.add_active_job(
name=name,
cron_expression=str(cron_expression) if cron_expression else None,
payload=payload,
description=note,
run_once=run_once,
run_at=run_at_dt,
)
try:
job = await cron_mgr.add_active_job(
name=name,
cron_expression=str(cron_expression) if cron_expression else None,
payload=payload,
description=note,
run_once=run_once,
run_at=run_at_dt,
)
except CronJobSchedulingError:
return "error: failed to schedule task due to invalid configuration."
next_run = job.next_run_time or run_at_dt
suffix = (
f"one-time at {next_run}"
@@ -195,7 +199,10 @@ class FutureTaskTool(FunctionTool[AstrAgentContext]):
updates["cron_expression"] = cron_expression
updates["payload"] = payload
job = await cron_mgr.update_job(str(job_id), **updates)
try:
job = await cron_mgr.update_job(str(job_id), **updates)
except CronJobSchedulingError:
return "error: failed to update task due to invalid configuration."
if not job:
return f"error: cron job {job_id} not found."
return f"Updated future task {job.job_id} ({job.name})."

View File

@@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from astrbot.core.cron.manager import CronJobManager
from astrbot.core.cron.manager import CronJobManager, CronJobSchedulingError
from astrbot.core.db.po import CronJob
@@ -190,24 +190,25 @@ class TestAddActiveJob:
@pytest.mark.asyncio
async def test_add_active_job_run_once(self, cron_manager, mock_db, sample_cron_job):
"""Test adding a run-once active job."""
"""Test adding a run-once active job with an invalid returned job."""
sample_cron_job.job_type = "active_agent"
sample_cron_job.run_once = True
mock_db.create_cron_job.return_value = sample_cron_job
run_at = datetime.now(timezone.utc) + timedelta(days=30)
result = await cron_manager.add_active_job(
name="Test Run Once Job",
cron_expression=None,
payload={"session": "test:group:123"},
run_once=True,
run_at=run_at,
)
with pytest.raises(CronJobSchedulingError, match="Invalid isoformat string"):
await cron_manager.add_active_job(
name="Test Run Once Job",
cron_expression=None,
payload={"session": "test:group:123"},
run_once=True,
run_at=run_at,
)
assert result == sample_cron_job
call_kwargs = mock_db.create_cron_job.call_args.kwargs
assert call_kwargs["run_once"] is True
assert call_kwargs["payload"]["run_at"] == run_at.isoformat()
class TestUpdateJob: