{"abstract":"A literal plus becomes a space, or a double-encoded slash unexpectedly becomes a path separator.","category":"Protocols","checks":8,"contract":"Decode a URL path component once using UTF-8. Preserve literal plus signs and malformed percent tokens; return None when encoded bytes are invalid UTF-8. Do not normalize paths or separators.","evaluation_group":"model-03bf64cb400904dc","failed_approach":"Decoding until the text stops changing interprets encoded percent signs as instructions for another pass.","family":"parse-percent-decode-once","id":"FA-211","implementations":{"attempt":{"sha256":"e18f6174cecfbd2d7bcaf9d060e3468a509dcaa12bac9b62a9fe346ae468b78c","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nfrom urllib.parse import unquote, unquote_plus\nN = 1\nobservations = []\ndef solve(text):\n    try:\n        while True:\n            decoded = unquote(text, errors='strict')\n            if decoded == text:\n                return decoded\n            text = decoded\n    except UnicodeDecodeError:\n        return None\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nlabel = 'a' * N\ncheck('literal plus retained in path', solve(label + '+b'), label + '+b')\ncheck('encoded slash decoded once', solve(label + '%2Fb'), label + '/b')\ncheck('double-encoded slash remains encoded', solve(label + '%252Fb'), label + '%2Fb')\ncheck('encoded plus becomes literal plus', solve('%2B' * N), '+' * N)\ncheck('UTF-8 encoded character', solve('%C3%A9' * N), 'é' * N)\ncheck('malformed percent token preserved', solve(label + '%QZ%'), label + '%QZ%')\ncheck('invalid UTF-8 rejected', solve('%FF'), None)\ncheck('empty path component', solve(''), '')\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"b41b6d0e7846f34fc295afae1d547210b3d90a3352c1335006b90687471090bb","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nfrom urllib.parse import unquote, unquote_plus\nN = 1\nobservations = []\ndef solve(text):\n    try:\n        return unquote_plus(text, errors='strict')\n    except UnicodeDecodeError:\n        return None\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nlabel = 'a' * N\ncheck('literal plus retained in path', solve(label + '+b'), label + '+b')\ncheck('encoded slash decoded once', solve(label + '%2Fb'), label + '/b')\ncheck('double-encoded slash remains encoded', solve(label + '%252Fb'), label + '%2Fb')\ncheck('encoded plus becomes literal plus', solve('%2B' * N), '+' * N)\ncheck('UTF-8 encoded character', solve('%C3%A9' * N), 'é' * N)\ncheck('malformed percent token preserved', solve(label + '%QZ%'), label + '%QZ%')\ncheck('invalid UTF-8 rejected', solve('%FF'), None)\ncheck('empty path component', solve(''), '')\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"fixed":{"sha256":"3150261a0255d06a41aedbe0409319a87636e7f76679f4390fc13158d2019935","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nfrom urllib.parse import unquote, unquote_plus\nN = 1\nobservations = []\ndef solve(text):\n    try:\n        return unquote(text, errors='strict')\n    except UnicodeDecodeError:\n        return None\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nlabel = 'a' * N\ncheck('literal plus retained in path', solve(label + '+b'), label + '+b')\ncheck('encoded slash decoded once', solve(label + '%2Fb'), label + '/b')\ncheck('double-encoded slash remains encoded', solve(label + '%252Fb'), label + '%2Fb')\ncheck('encoded plus becomes literal plus', solve('%2B' * N), '+' * N)\ncheck('UTF-8 encoded character', solve('%C3%A9' * N), 'é' * N)\ncheck('malformed percent token preserved', solve(label + '%QZ%'), label + '%QZ%')\ncheck('invalid UTF-8 rejected', solve('%FF'), None)\ncheck('empty path component', solve(''), '')\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":" This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"parse-percent-decode-once","generated_at":"2026-09-29T14:36:51.476199+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Different decoding layers can disagree about a resource path. Keeping the decode count and plus-sign convention explicit prevents ambiguous interpretation.","repair":"Decode percent-encoded bytes exactly once with strict UTF-8 handling; preserve literal plus characters.","root_cause":"Form-urlencoded rules or repeated decoding are applied to a path component that requires one decode pass.","sha256":"67f45cf34b1344a1fd642d03fa1322c01378ee69bbb69b2b1eec3dcfeebca8bc","title":"A second percent-decoding pass changes path data into syntax · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":32.582,"exit_code":1,"observations":[{"actual":"a+b","check":"literal plus retained in path","expected":"a+b","passed":true},{"actual":"a/b","check":"encoded slash decoded once","expected":"a/b","passed":true},{"actual":"a/b","check":"double-encoded slash remains encoded","expected":"a%2Fb","passed":false},{"actual":"+","check":"encoded plus becomes literal plus","expected":"+","passed":true},{"actual":"é","check":"UTF-8 encoded character","expected":"é","passed":true},{"actual":"a%QZ%","check":"malformed percent token preserved","expected":"a%QZ%","passed":true},{"actual":null,"check":"invalid UTF-8 rejected","expected":null,"passed":true},{"actual":"","check":"empty path component","expected":"","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"literal plus retained in path\", \"actual\": \"a+b\", \"expected\": \"a+b\", \"passed\": true}, {\"check\": \"encoded slash decoded once\", \"actual\": \"a/b\", \"expected\": \"a/b\", \"passed\": true}, {\"check\": \"double-encoded slash remains encoded\", \"actual\": \"a/b\", \"expected\": \"a%2Fb\", \"passed\": false}, {\"check\": \"encoded plus becomes literal plus\", \"actual\": \"+\", \"expected\": \"+\", \"passed\": true}, {\"check\": \"UTF-8 encoded character\", \"actual\": \"é\", \"expected\": \"é\", \"passed\": true}, {\"check\": \"malformed percent token preserved\", \"actual\": \"a%QZ%\", \"expected\": \"a%QZ%\", \"passed\": true}, {\"check\": \"invalid UTF-8 rejected\", \"actual\": null, \"expected\": null, \"passed\": true}, {\"check\": \"empty path component\", \"actual\": \"\", \"expected\": \"\", \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":33.214,"exit_code":1,"observations":[{"actual":"a b","check":"literal plus retained in path","expected":"a+b","passed":false},{"actual":"a/b","check":"encoded slash decoded once","expected":"a/b","passed":true},{"actual":"a%2Fb","check":"double-encoded slash remains encoded","expected":"a%2Fb","passed":true},{"actual":"+","check":"encoded plus becomes literal plus","expected":"+","passed":true},{"actual":"é","check":"UTF-8 encoded character","expected":"é","passed":true},{"actual":"a%QZ%","check":"malformed percent token preserved","expected":"a%QZ%","passed":true},{"actual":null,"check":"invalid UTF-8 rejected","expected":null,"passed":true},{"actual":"","check":"empty path component","expected":"","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"literal plus retained in path\", \"actual\": \"a b\", \"expected\": \"a+b\", \"passed\": false}, {\"check\": \"encoded slash decoded once\", \"actual\": \"a/b\", \"expected\": \"a/b\", \"passed\": true}, {\"check\": \"double-encoded slash remains encoded\", \"actual\": \"a%2Fb\", \"expected\": \"a%2Fb\", \"passed\": true}, {\"check\": \"encoded plus becomes literal plus\", \"actual\": \"+\", \"expected\": \"+\", \"passed\": true}, {\"check\": \"UTF-8 encoded character\", \"actual\": \"é\", \"expected\": \"é\", \"passed\": true}, {\"check\": \"malformed percent token preserved\", \"actual\": \"a%QZ%\", \"expected\": \"a%QZ%\", \"passed\": true}, {\"check\": \"invalid UTF-8 rejected\", \"actual\": null, \"expected\": null, \"passed\": true}, {\"check\": \"empty path component\", \"actual\": \"\", \"expected\": \"\", \"passed\": true}], \"passed\": false}\n"},"fixed":{"elapsed_ms":34.935,"exit_code":0,"observations":[{"actual":"a+b","check":"literal plus retained in path","expected":"a+b","passed":true},{"actual":"a/b","check":"encoded slash decoded once","expected":"a/b","passed":true},{"actual":"a%2Fb","check":"double-encoded slash remains encoded","expected":"a%2Fb","passed":true},{"actual":"+","check":"encoded plus becomes literal plus","expected":"+","passed":true},{"actual":"é","check":"UTF-8 encoded character","expected":"é","passed":true},{"actual":"a%QZ%","check":"malformed percent token preserved","expected":"a%QZ%","passed":true},{"actual":null,"check":"invalid UTF-8 rejected","expected":null,"passed":true},{"actual":"","check":"empty path component","expected":"","passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"literal plus retained in path\", \"actual\": \"a+b\", \"expected\": \"a+b\", \"passed\": true}, {\"check\": \"encoded slash decoded once\", \"actual\": \"a/b\", \"expected\": \"a/b\", \"passed\": true}, {\"check\": \"double-encoded slash remains encoded\", \"actual\": \"a%2Fb\", \"expected\": \"a%2Fb\", \"passed\": true}, {\"check\": \"encoded plus becomes literal plus\", \"actual\": \"+\", \"expected\": \"+\", \"passed\": true}, {\"check\": \"UTF-8 encoded character\", \"actual\": \"é\", \"expected\": \"é\", \"passed\": true}, {\"check\": \"malformed percent token preserved\", \"actual\": \"a%QZ%\", \"expected\": \"a%QZ%\", \"passed\": true}, {\"check\": \"invalid UTF-8 rejected\", \"actual\": null, \"expected\": null, \"passed\": true}, {\"check\": \"empty path component\", \"actual\": \"\", \"expected\": \"\", \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}