{"record":{"id":"52dadb4eb1e5c2a6","repo":"microsoft/semantic-kernel","slug":"the-thread-could-not-be-deleted-due-to-an-error-re-52dadb","errorCode":null,"errorMessage":"The thread could not be deleted due to an error response from the service.","messagePattern":"The thread could not be deleted due to an error response from the service\\.","errorType":"exception","errorClass":"AgentThreadOperationException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/agents/open_ai/openai_assistant_agent.py","lineNumber":190,"sourceCode":"                messages=self._messages,\n                metadata=self._metadata,\n                tool_resources=self._tool_resources,\n            )\n        except Exception as ex:\n            raise AgentThreadOperationException(\n                \"The thread could not be created due to an error response from the service.\"\n            ) from ex\n        return response.id\n\n    @override\n    async def _delete(self) -> None:\n        \"\"\"Ends the current thread.\"\"\"\n        if self._id is None:\n            raise AgentThreadOperationException(\"The thread cannot be deleted because it has not been created yet.\")\n        try:\n            await self._client.beta.threads.delete(self._id)\n        except Exception as ex:\n            raise AgentThreadOperationException(\n                \"The thread could not be deleted due to an error response from the service.\"\n            ) from ex\n\n    @override\n    async def _on_new_message(self, new_message: str | ChatMessageContent) -> None:\n        \"\"\"Called when a new message has been contributed to the chat.\"\"\"\n        if isinstance(new_message, str):\n            new_message = ChatMessageContent(role=AuthorRole.USER, content=new_message)\n\n        # Only add the message to the thread if it's not already there\n        if (\n            not new_message.metadata\n            or \"thread_id\" not in new_message.metadata\n            or new_message.metadata[\"thread_id\"] != self._id\n        ):\n            assert self._id is not None  # nosec\n            await AssistantThreadActions.create_message(self._client, self._id, new_message)\n","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/agents/open_ai/openai_assistant_agent.py#L172-L208","documentation":"Raised as AgentThreadOperationException by AssistantAgentThread._delete when the underlying client.beta.threads.delete() call throws. The original service exception is chained (from ex), preserving the real cause (auth, not-found, network, rate limit).","triggerScenarios":"Calling thread.delete() (valid id present) when the OpenAI service rejects the delete: the thread id no longer exists, credentials are invalid/expired, rate limited, or a network error occurs.","commonSituations":"Deleting a thread that was already deleted; expired token; race condition where the thread was removed elsewhere; transient network failures.","solutions":["Inspect the chained __cause__ for the actual status (404 = already gone, 401/403 = auth, 429 = rate limit).","Treat 404 as benign in cleanup (the thread is already deleted).","Retry with backoff for 429/5xx; refresh credentials for 401/403.","Avoid double-deleting; track deletion state."],"exampleFix":"# before\ntry:\n    await thread.delete()\nexcept AgentThreadOperationException:\n    pass  # swallows real cause\n# after\ntry:\n    await thread.delete()\nexcept AgentThreadOperationException as e:\n    cause = e.__cause__\n    if getattr(cause, \"status_code\", None) == 404:\n        logging.info(\"thread already deleted\")\n    else:\n        raise","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"import asyncio\nfrom semantic_kernel.exceptions.agent_exceptions import AgentThreadOperationException\n\nasync def safe_delete(thread, attempts=3):\n    for i in range(attempts):\n        try:\n            return await thread.delete()\n        except AgentThreadOperationException as e:\n            cause = e.__cause__\n            status = getattr(cause, \"status_code\", None)\n            if status == 404:\n                logging.info(\"thread already deleted\")\n                return\n            if status in (429, 500, 502, 503) and i < attempts - 1:\n                await asyncio.sleep(2 ** i)\n                continue\n            raise","preventionTips":["Treat a 404 cause as benign during cleanup (already deleted).","Retry transient 429/5xx with backoff.","Track deletion state to avoid double-delete attempts."],"tags":["openai","semantic-kernel","agents","threads","network","service-error","retry"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}