BUG: MemoryInterface.get_request_from_response fails with IndexError for non-zero starting sequence or sequence gaps
Maintainers usually reply within 2 days
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 25/100
Research direction
The bug is in _execute_get_request_from_response in pyrit/memory/memory_interface.py, which indexes the conversation list by response.sequence - 1. Read _execute_get_conversation_messages to see how messages are ordered, then reproduce with the SQLite script in the issue. Pull request #3190 is already open against this issue, so check it before starting a new fix. Done means the method returns the message with the closest lower sequence, or raises a clear error when none exists.
Written by the indexing model from the issue text.
Description
Describe the bug
In pyrit/memory/memory_interface.py, MemoryInterface._execute_get_request_from_response retrieves the request that produced a given assistant response:
def _execute_get_request_from_response(self, *, response: Message) -> Message:
if response.api_role != "assistant":
raise ValueError("The provided request is not a response (role must be 'assistant').")
if response.sequence < 1:
raise ValueError("The provided request does not have a preceding request (sequence < 1).")
conversation = self._dispatch_memory_operation(
"get_conversation_messages",
self._execute_get_conversation_messages,
conversation_id=response.conversation_id,
)
return conversation[response.sequence - 1]
The method treats response.sequence - 1 as a 0-based list index into conversation. However, get_conversation_messages returns messages ordered by sequence (sorted(conversation_by_sequence.keys())), where list index corresponds to sequence number only if the conversation started at sequence 0 and contains no sequence gaps.
Whenever:
- A conversation starts at a non-zero sequence (e.g. using
starting_sequenceinSeedPrompt.from_messagesorSimulatedConversationAttack), or - A conversation contains sequence gaps (e.g. after pruning or retries where pieces were deleted),
calling get_request_from_response (or get_request_from_response_async, or DecodingScorer which invokes this method) raises:
IndexError: list index out of range (or returns the wrong message if the index exists but does not align).
Steps/Code to Reproduce
import asyncio
from uuid import uuid4
from pyrit.memory import SQLiteMemory
from pyrit.models import MessagePiece
async def test_reproduce():
memory = SQLiteMemory(db_path=":memory:")
conversation_id = str(uuid4())
# Create a conversation with starting_sequence=10
pieces = [
MessagePiece(
role="user",
original_value="What is the capital of France?",
conversation_id=conversation_id,
sequence=10,
),
MessagePiece(
role="assistant",
original_value="Paris.",
conversation_id=conversation_id,
sequence=11,
),
]
await memory.add_message_pieces_to_memory_async(message_pieces=pieces)
conversation = await memory.get_conversation_messages_async(conversation_id=conversation_id)
response = conversation[1] # sequence=11
# Attempt to retrieve the request that produced the response
await memory.get_request_from_response_async(response=response)
asyncio.run(test_reproduce())
Expected Results
get_request_from_response_async returns the message immediately preceding the response in the conversation timeline (Message(role='user', sequence=10, ...)).
Actual Results
Traceback (most recent call last):
...
File "pyrit/memory/memory_interface.py", line 3041, in _execute_get_request_from_response
return conversation[response.sequence - 1]
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
IndexError: list index out of range
Suggested Fix
Instead of assuming conversation index equals sequence - 1, locate the preceding message in conversation with sequence < response.sequence:
conversation = self._dispatch_memory_operation(
"get_conversation_messages",
self._execute_get_conversation_messages,
conversation_id=response.conversation_id,
)
preceding_messages = [msg for msg in conversation if msg.sequence < response.sequence]
if not preceding_messages:
raise ValueError(
f"The provided request does not have a preceding request in conversation '{response.conversation_id}'."
)
return preceding_messages[-1]
- Dominant language
- Python
- Stars
- 4.6k
- Forks
- 944
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 328
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/PyRIT
-
BUG UnicodeReplacementConverter writes characters outside the BMP as invalid five-digit \u escapesPossibly taken @ShivamSharma43 claimed this 2 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
microsoft/PyRIT#3095 · 3 comments ·
Maintainers usually reply within 2 days
-
BUG Configuration keeps runtime-status errors after polling recoversPossibly taken @joeyvictorino claimed this 3 days ago. OpenBug: triage GUI help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
microsoft/PyRIT#2868 · 4 comments · 1 assignee ·
Maintainers usually reply within 2 days
-
Conversation editor never revokes blob URLs of removed attachmentsPossibly taken @ManoharPaturi claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 25/100
Maintainers usually reply within 2 days
-
Scenario progress double-counts an attack result after its timestamp is bumpedPossibly taken @ManoharPaturi claimed this today. Open
Difficulty 3/5 Half a day Newbie friendliness 25/100
Maintainers usually reply within 2 days
Similar issues
-
vis.image() reports "Unsupported number of image channels: 224" for an HxWxC imagePossibly taken @piyush182004 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
fossasia/visdom#1997 · 3 comments ·
Maintainers usually reply within 6 days
-
Byse new domainOpen
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Gujal00/ResolveURL#1525 ·
Maintainers usually reply within 1 day
-
Serialisation fails with pydantic 2.14: wrap serialisers pass SerializationInfo as index_keyPossibly taken @hugohills-regnosys claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
finos/rune-python-runtime#45 ·
-
[Bug]: LangGraph Store sync put bypasses namespace validationPossibly taken @ktz03 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
volcengine/OpenViking#6050 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
hummingbot/condor#283 ·
Maintainers usually reply within 1 day