Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/Exceptions/Social/XPublishException.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ public static function fromApiResponse(mixed $response): static
);
}

if ($statusCode === 413) {
return new static(
userMessage: 'Media chunk rejected by X (payload too large).',
category: ErrorCategory::MediaFormat,
platformErrorCode: (string) $statusCode,
rawResponse: $rawResponse,
);
}

if (in_array($statusCode, [500, 502, 503, 504], true)) {
return new static(
userMessage: 'X server error. Please try again later.',
Expand Down
9 changes: 6 additions & 3 deletions app/Services/Social/XPublisher.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,11 @@ private function chunkedUpload(string $tempFile, int $totalBytes, string $mimeTy
throw new \Exception('No media_id returned from INIT');
}

// APPEND - Read from temp file in 5MB chunks (memory-safe)
$chunkSize = 5 * 1024 * 1024;
// APPEND - Read from temp file in 1MB chunks. Matches the
// twitter-api-v2 SDK default and X's own quickstart examples;
// larger chunks (we previously used 5MB) trigger 413 at the X
// edge with an empty body, surfacing as "An unknown X error".
$chunkSize = 1024 * 1024;
$handle = fopen($tempFile, 'r');
$index = 0;

Expand All @@ -220,7 +223,7 @@ private function chunkedUpload(string $tempFile, int $totalBytes, string $mimeTy

$appendResponse = $this->socialHttp()->withToken($this->accessToken)
->timeout(300)
->attach('media', $chunk, 'chunk'.$index)
->attach('media', $chunk, 'chunk'.$index, ['Content-Type' => $mimeType])
->post("{$this->baseUrl}/media/upload/{$mediaId}/append", [
'segment_index' => $index,
]);
Expand Down
12 changes: 12 additions & 0 deletions tests/Unit/Exceptions/Social/XPublishExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@
->and($exception->userMessage)->toBe('X server error. Please try again later.');
});

test('HTTP 413 with empty body maps to MediaFormat category', function () {
$response = Http::response('', 413);

$fakeResponse = Http::fake(['*' => $response])->post('https://api.x.com/test');

$exception = XPublishException::fromApiResponse($fakeResponse);

expect($exception->category)->toBe(ErrorCategory::MediaFormat)
->and($exception->userMessage)->toBe('Media chunk rejected by X (payload too large).')
->and($exception->platformErrorCode)->toBe('413');
});

test('unknown type maps to Unknown category with detail as message', function () {
$response = Http::response([
'type' => 'https://api.x.com/2/problems/some-unknown-problem',
Expand Down
Loading