fix: url-decode path segments in route layer#223
Merged
Conversation
After PR #216, durable-execution ARNs minted by Execution.new() contain a literal '/' of the form "<uuid>/<invocation-id>". boto's rest-json serializer percent-encodes '/' as %2F in the non-greedy {DurableExecutionArn} URI label, so paths arriving at the local WebServer look like: /2025-12-01/durable-executions/<uuid>%2F<invocation-id> The same shape applies to ListDurableExecutionsByFunction with function names like "MyFunction:$LATEST" (':' -> %3A, '$' -> %24). Without decoding, store lookups never match the key and every Get/State/History/Checkpoint/Stop returns 404. List queries silently return an empty result set. - Decode each segment once in Route.from_string. raw_path is kept as the original wire string for logging. Splitting on '/' happens before decoding so a captured value containing %2F stays inside its segment instead of acting as a path separator. - Remove the now-redundant per-route unquote() calls from the three callback routes (added in #117 for the same bug shape). - Add a real-boto regression test under tests/web/e2e/ that drives a live WebServer for every affected operation with values containing the characters boto percent-encodes. Closes the test-coverage gap that let the bug ship. - Strengthen test_route_with_special_characters to assert both segments[N] and the named field are decoded while raw_path keeps the wire form. Affects users running WebRunner / dex-local-runner against their durable function in RIE; pre-fix, the function 404s on its first checkpoint after upgrading to 1.2.0. Closes #222
bchampp
approved these changes
May 21, 2026
Contributor
Author
|
some local-runner tests I ran: Unfixed (origin/main / shipped v1.2.0): get_durable_execution FAIL: 404 Execution %2Finv-unfixed not found Fixed (this PR): get_durable_execution OK (Status=RUNNING, ARN echoes back) |
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.
Issue #, if available: #222
Description of changes:
dex-local-runner(WebRunner) returned404for every Get/State/History/Checkpoint/Stop call against an execution whose ARN contained a literal/, and silently returned an empty list forListDurableExecutionsByFunctionwhen the function name contained:or$. boto's rest-json serializer percent-encodes those characters in non-greedy URI labels (e.g./→%2F,:→%3A,$→%24), but the WebServer's route layer wasn't decoding the captured segment before using it as a store-lookup key.This PR centralizes URL-decoding at the parser layer so every captured path segment inherits the behavior:
src/.../web/routes.py— decode each segment inRoute.from_string.raw_pathis preserved as the original wire path for logging. Order matters: split on the literal/first so%2F-encoded slashes inside captured values stay inside their segment instead of acting as path separators. The three callback routes drop their now-redundant per-callsiteunquote()calls (added in fix: decode callback id in routes #117 for the same bug shape).tests/web/routes_test.py— strengthentest_route_with_special_charactersto assert bothsegments[N]and the named field are decoded, whileraw_pathkeeps the wire form.tests/web/e2e/routes_arn_encoding_int_test.py(new) — drive a realboto3Lambda client against a liveWebServerfor every affected operation, with values containing the characters boto percent-encodes. Closes the test-coverage gap that let the bug ship: no existing test ran realistic boto serialization through the route layer.Affected runners
WebRunner/dex-local-runner— fixed.DurableFunctionTestRunner(in-process) — unaffected; usesInMemoryServiceClientwhich ignoresdurable_execution_arn.DurableFunctionCloudTestRunner— unaffected; ARNs come from the real Lambda backend.Verification
hatch test— 1245 passed (1239 prior + 6 new regression tests).hatch fmt --check— clean.hatch run types:check— clean.routes.pycauses 7 tests (6 integration + the strengthened unit test) to fail with%XX-in-error or wire-form-inequality messages, confirming the regression coverage is real.Closes #222.
Dependencies
If this PR requires testing against a specific branch of the Python Language SDK (e.g., for unreleased changes), uncomment and specify the branch below. Otherwise, leave commented to use the main branch.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.