From e2139fd91bad78781f105a8114ccf69abc9e051b Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Wed, 29 Apr 2026 07:37:04 +0800 Subject: [PATCH] fix: guard against None system_prompt in _ensure_persona_and_skills ProviderRequest.system_prompt defaults to None. When a persona with a prompt is configured, _ensure_persona_and_skills calls ``req.system_prompt += ...`` which crashes with ``TypeError`` when system_prompt is None. Added a None guard before the persona prompt injection and skills prompt appending sections so they always operate on a string. --- astrbot/core/astr_main_agent.py | 3 ++ tests/unit/test_system_prompt_none_bug.py | 51 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/unit/test_system_prompt_none_bug.py diff --git a/astrbot/core/astr_main_agent.py b/astrbot/core/astr_main_agent.py index b013f010e..daef9e483 100644 --- a/astrbot/core/astr_main_agent.py +++ b/astrbot/core/astr_main_agent.py @@ -399,6 +399,9 @@ async def _ensure_persona_and_skills( event, extract_persona_custom_error_message_from_persona(persona) ) + if req.system_prompt is None: + req.system_prompt = "" + if persona: # Inject persona system prompt if prompt := persona["prompt"]: diff --git a/tests/unit/test_system_prompt_none_bug.py b/tests/unit/test_system_prompt_none_bug.py new file mode 100644 index 000000000..434fccf67 --- /dev/null +++ b/tests/unit/test_system_prompt_none_bug.py @@ -0,0 +1,51 @@ +"""Test that _ensure_persona_and_skills handles None system_prompt. + +When ProviderRequest.system_prompt is None (the default), calling +_ensure_persona_and_skills with a persona that has a prompt should +not crash with ``TypeError: unsupported operand type(s) for +=``. + +The bug was that ``req.system_prompt += ...`` was called when +system_prompt was None instead of initializing it to "" first. +""" + +import pytest +from unittest.mock import AsyncMock, MagicMock + + +@pytest.mark.asyncio +async def test_ensure_persona_and_skills_handles_none_system_prompt(): + from astrbot.core.astr_main_agent import _ensure_persona_and_skills + from astrbot.core.provider.entities import ProviderRequest + from astrbot.core.db.po import ConversationV2 + + # ProviderRequest.system_prompt defaults to None + req = ProviderRequest() + req.conversation = ConversationV2( + conversation_id="test", + platform_id="test", + user_id="test", + ) + + cfg = {"computer_use_runtime": "local"} + + plugin_context = MagicMock() + plugin_context.persona_manager.resolve_selected_persona = AsyncMock( + return_value=( + "persona-1", + {"prompt": "You are a helpful assistant.", "name": "test"}, + None, + False, + ) + ) + + event = MagicMock() + event.unified_msg_origin = "test:test_user" + + # This should NOT raise TypeError even though req.system_prompt is None + try: + await _ensure_persona_and_skills(req, cfg, plugin_context, event) + except TypeError as e: + pytest.fail(f"_ensure_persona_and_skills crashed with TypeError: {e}") + + assert req.system_prompt is not None + assert "# Persona Instructions" in req.system_prompt