Repository navigation
Python: fix(filesystem): bound the literal storage-key branch by the segment cap - #9137
Closed
Winoren (Winoren) wants to merge 1 commit into
Closed
Winoren (Winoren) wants to merge 1 commit into
Winoren (Winoren) wants to merge 1 commit into
Conversation
_storage_key_segment defines _MAX_ENCODED_STORAGE_KEY_SEGMENT_LENGTH and both the base32 and digest branches honor it, but the literal branch returned any literal-safe value verbatim with no upper bound. A 308-character session id therefore reached the filesystem unchanged and failed there with ENAMETOOLONG once the caller appended its extension, turning a valid id into a failed write. Apply the same bound to the literal branch so longer literal-safe values fall through to the encoded forms like any other over-long value. No data migration is affected: names in the newly-encoded range were already unwritable.
Winoren (Winoren)
temporarily deployed
to
github-app-auth
October 7, 2026 09:04 — with
GitHub Actions
Inactive
Winoren (Winoren)
had a problem deploying
to
github-app-auth
October 7, 2026 09:04 — with
GitHub Actions
Error
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Valid existing storage names over 180 characters become unreachable without migration or legacy lookup support.
Review effort: Balanced
Findings: 1
Open (3)
What changed in this PR
Bounds literal storage-key segments to prevent filesystem name-length failures.
Changes:
- Applies the 180-character cap to literal-safe identifiers.
- Adds boundary, collision-resistance, prefix, and filesystem tests.
| File | Description |
|---|---|
python/packages/core/agent_framework/_filesystem.py |
Bounds literal storage-key segments. |
python/packages/core/tests/core/test_filesystem.py |
Adds regression and boundary coverage. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
| A single path segment containing no path separators. | ||
| """ | ||
| if _is_literal_storage_key_segment_safe(value): | ||
| if _is_literal_storage_key_segment_safe(value) and len(value) <= _MAX_ENCODED_STORAGE_KEY_SEGMENT_LENGTH: |
Comment on lines
+294
to
+299
| def test_length_bounded_literals_stay_injective() -> None: | ||
| """Moving long literals into the encoded namespace must preserve injectivity. | ||
|
|
||
| The segment is an isolation boundary, so two distinct over-long identifiers | ||
| may not be folded together by the change -- not even after case folding. | ||
| """ |
Comment on lines
+310
to
+313
| A 308-character literal-safe session id previously produced a 308-character | ||
| file stem, which ``open()`` rejects with ``ENAMETOOLONG`` (errno 63) on | ||
| macOS, Linux and Windows alike -- turning a valid session id into a failed | ||
| write. Writing ``stem + extension`` is exactly what |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Fixes #9136
Problem
_storage_key_segmentderives a single path segment that callers turn into a filename or directory name. The module defines_MAX_ENCODED_STORAGE_KEY_SEGMENT_LENGTH = 180and documents the cap as existing "so the segment stays within filesystem name limits", and two of its three branches honor it. The literal branch does not:A literal-safe value can be of any length, so a long-but-otherwise-perfectly-safe identifier reaches the filesystem unchanged and fails there instead of being encoded.
FileHistoryProvider.save_messagesis one concrete path._sessions.py:2548-2549appends.jsonlto the derived stem, and_sessions.py:2466then opens it:The same applies to every other caller that appends its own suffix:
_file_access.py:1915(session directory name),_file_memory.py:328,_todo.py:354,_memory.py:753, and_history_provider.py:219-236.Fix
Apply the bound the other two branches already use, so longer literal-safe values fall through to the encoded forms:
This reads as an oversight rather than a deliberate exception, which is why it is worth fixing at the derivation point instead of guarding at each call site.
No data migration
Only values in the 181-249 byte range change their on-disk name. Those names were already unwritable before this change, because the caller appends an extension, so no existing file can carry one.
Verification
len >= 181moving fromliteraltodigest. No non-regex-driven behavioral change: the_is_literal_storage_key_segment_safepredicate itself is untouched (0 observations changed on that field), and the base32 and digest branches are unchanged.~scope-,~session-,~todo-,~memory-,~access-): 87 distinct values still fold to 87 distinct segments, including after case folding. The segment is an isolation boundary, so this matters.mkdirfrom 8 to 0, and failedopenfrom 10 to 0.test_filesystem.py(no new file): 42 passed. The five downstream caller suites (test_sessions,test_harness_file_access,test_harness_file_memory,test_harness_todo,test_harness_memory) also pass.OSError: [Errno 63] File name too long, and pass again once the fix is restored.The new cases cover the length bound, the boundary (
<= capmust stay verbatim so existing on-disk layouts keep working), the cap across all five prefixes, injectivity of the newly-encoded range, and a realopen()on a derived name.Note that the existing
SAFE_IDENTIFIERSentry"x" * 200is updated to sit exactly at the cap, since the fix makes its original expectation (verbatim) impossible.Risk
Low. Values at or under the cap are byte-for-byte unchanged. The
.NETside has no equivalent defect:FoundryAgentSessionKeyEncoder.BuildStorageKeyhashes unconditionally and is bounded by construction.