Skip to content

Commit 80a3c96

Browse files
committed
Clarify ambiguous 413 scan failures
1 parent 800836c commit 80a3c96

3 files changed

Lines changed: 40 additions & 17 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
- The 200-character cap on the commit message now applies to the value read from the
88
repository, not only to `--commit-message`. A truncated message ends in `...` and the
99
truncation is reported at INFO.
10-
- A full scan refused for its size (HTTP 413, 414 or 431) now reports which value to
11-
shorten.
10+
- A full scan refused for its size (HTTP 413, 414 or 431) now distinguishes possible
11+
upload-size and request-metadata causes and reports what to shorten.
1212

1313
## 2.9.6
1414

‎socketsecurity/core/__init__.py‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,10 @@
113113
FULL_SCAN_UPLOAD_MAX_ATTEMPTS = len(FULL_SCAN_UPLOAD_BACKOFF_SCHEDULE_SECONDS)
114114
FULL_SCAN_UPLOAD_BACKOFF_JITTER_SECONDS = 2.0
115115

116-
# Statuses that mean the request line itself was rejected before the API read it: the
117-
# scan metadata (commit message, branch, committers) travels in the query string of the
118-
# full-scan POST, so an oversized value is refused by the proxy in front of the API. The
119-
# proxy picks the code -- 413 (payload), 414 (URI), 431 (headers) -- so all three map to
120-
# the same cause. Not transient: every retry sends the same oversized URL.
116+
# Statuses that mean the request is too large to process. Scan metadata travels in the
117+
# query string of the full-scan POST, while manifests travel in its multipart body. A
118+
# 413 can refer to either part; 414 points to the URL, and some proxies report 431 when
119+
# the encoded request target exceeds their header limit. None are transient.
121120
REQUEST_TOO_LARGE_STATUS_CODES = (413, 414, 431)
122121

123122
# Diff-scan polling policy. The legacy scan comparison (fullscans.stream_diff) holds a
@@ -1126,12 +1125,20 @@ def create_full_scan(self, files: List[str], params: FullScanParams, base_paths:
11261125
break
11271126
except APIFailure as error:
11281127
if error.status_code in REQUEST_TOO_LARGE_STATUS_CODES:
1128+
if error.status_code == 413:
1129+
guidance = (
1130+
"The response does not distinguish between an oversized multipart "
1131+
"upload and oversized scan metadata in the request URL. Reduce the "
1132+
"uploaded scan inputs, or pass a shorter --commit-message."
1133+
)
1134+
else:
1135+
guidance = (
1136+
"Scan metadata is sent in the request URL. Pass a shorter "
1137+
"--commit-message or shorten other scan metadata."
1138+
)
11291139
raise APIFailure(
11301140
f"Full scan request rejected as too large (HTTP {error.status_code}). "
1131-
"Scan metadata is sent in the request URL, so an oversized value -- "
1132-
"most often the commit message -- is refused by the proxy in front of "
1133-
"the API before the request is read. Pass a shorter --commit-message "
1134-
f"to work around it.\n{error}",
1141+
f"{guidance}\n{error}",
11351142
status_code=error.status_code,
11361143
) from error
11371144
if backoff_seconds is None or not error.is_transient_error():

‎tests/unit/test_full_scan_retry.py‎

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,15 +285,13 @@ def test_retry_decision_delegates_to_sdk_classification(
285285
assert core_with_mock_sdk.sdk.fullscans.post.call_count == expected_calls
286286

287287

288-
@pytest.mark.parametrize("status_code", [413, 414, 431])
289-
def test_oversized_request_is_not_retried_and_names_the_cause(
288+
@pytest.mark.parametrize("status_code", [414, 431])
289+
def test_oversized_request_target_is_not_retried_and_names_the_cause(
290290
core_with_mock_sdk, tmp_path, no_sleep, status_code
291291
):
292292
"""
293-
A proxy that refuses the request line reports 413, 414 or 431 depending on which limit
294-
it checks. None of them are worth a retry (the same oversized URL goes back out), and
295-
the SDK's own message is a status code plus the proxy's response body, which does not
296-
say what to change.
293+
URI and header size failures are deterministic for the same request, and the SDK's
294+
message does not say which metadata to shorten.
297295
"""
298296
manifest = tmp_path / "package.json"
299297
manifest.write_text("{}")
@@ -310,3 +308,21 @@ def test_oversized_request_is_not_retried_and_names_the_cause(
310308
# The SDK's original text is kept so the proxy's own response stays available.
311309
assert f"original_status_code:{status_code}" in message
312310
assert exc_info.value.status_code == status_code
311+
312+
313+
def test_413_reports_upload_and_metadata_causes(core_with_mock_sdk, tmp_path, no_sleep):
314+
manifest = tmp_path / "package.json"
315+
manifest.write_text("{}")
316+
core_with_mock_sdk.sdk.fullscans.post.side_effect = _catch_all_failure(413)
317+
318+
with pytest.raises(APIFailure) as exc_info:
319+
core_with_mock_sdk.create_full_scan([str(manifest)], MagicMock())
320+
321+
assert core_with_mock_sdk.sdk.fullscans.post.call_count == 1
322+
no_sleep.assert_not_called()
323+
message = str(exc_info.value)
324+
assert "oversized multipart upload" in message
325+
assert "oversized scan metadata" in message
326+
assert "--commit-message" in message
327+
assert "original_status_code:413" in message
328+
assert exc_info.value.status_code == 413

0 commit comments

Comments
 (0)