{"record":{"id":"814740cc34b712cf","repo":"remotion-dev/remotion","slug":"could-not-cancel-render-renderid-exception","errorCode":null,"errorMessage":"Could not cancel render {$renderId}: {$exception->getMessage()}","messagePattern":"Could not cancel render (.+?): (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"packages/lambda-php/src/PHPClient.php","lineNumber":326,"sourceCode":"        }\n\n        if (($progress['cancellationEnabled'] ?? false) !== true) {\n            throw new InvalidArgumentException(\n                \"Cannot cancel render {$renderId}: The render was not started with enableCancellation: true.\"\n            );\n        }\n\n        try {\n            $this->writeCancellationToS3(\n                $bucketName,\n                \"renders/{$renderId}/cancel.json\",\n                json_encode(\n                    ['cancelledAt' => (int) floor(microtime(true) * 1000)],\n                    JSON_THROW_ON_ERROR\n                )\n            );\n        } catch (\\Throwable $exception) {\n            throw new Exception(\"Could not cancel render {$renderId}: {$exception->getMessage()}\", 0, $exception);\n        }\n    }\n\n    protected function readProgressFromS3(string $bucketName, string $key): string\n    {\n        $result = $this->createS3Client()->getObject([\n            'Bucket' => $bucketName,\n            'Key' => $key,\n        ]);\n\n        return (string) $result['Body'];\n    }\n\n    protected function writeCancellationToS3(string $bucketName, string $key, string $body): void\n    {\n        $this->createS3Client()->putObject([\n            'Bucket' => $bucketName,\n            'Key' => $key,","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/remotion-dev/remotion/blob/10db9de07356446fb0edb3c3ae211369b693d18b/packages/lambda-php/src/PHPClient.php#L308-L344","documentation":"cancelRenderOnLambda() passed the opt-in check and then wrote renders/{renderId}/cancel.json into the render bucket via writeCancellationToS3() (an S3 PutObject). Any Throwable from that AWS call - missing permissions, wrong bucket or region, network failure - is wrapped in a generic Exception 'Could not cancel render ...'; the previous exception carries the underlying AWS SDK error.","triggerScenarios":"The credentials/role used by PHPClient lack s3:PutObject on the bucket's renders/* prefix; the bucketName or region passed to PHPClient does not match the render's bucket; the bucket was deleted (NoSuchBucket); expired credentials; or a transient S3/network error during PutObject.","commonSituations":"Rendering with a privileged role but canceling from an ops tool that uses read-only credentials; hand-typed bucket names that differ from the one in the render response; bucket policies that restrict writes to specific principals; cross-region S3 calls failing.","solutions":["Unwrap the cause: inspect $exception->getPrevious() - the AWS error code (AccessDenied, NoSuchBucket, InvalidAccessKeyId, SlowDown) identifies the real problem.","Grant s3:PutObject on arn:aws:s3:::<bucket>/renders/* to the IAM user or role the PHP client runs with.","Verify the bucket name and region passed to PHPClient exactly match the values from the render response.","Retry the cancel once if the cause was transient (5xx, SlowDown, timeout) - the write only replaces cancel.json with a fresh timestamp and is idempotent."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-flight: confirm the canceling credentials can write to the render bucket\n$s3 = new \\Aws\\S3\\S3Client(['version' => 'latest', 'region' => $region, 'credentials' => $creds]);\ntry {\n    $s3->putObject([\n        'Bucket' => $bucketName,\n        'Key' => 'renders/_cancel-write-test',\n        'Body' => '{}',\n    ]);\n    $s3->deleteObject(['Bucket' => $bucketName, 'Key' => 'renders/_cancel-write-test']);\n} catch (\\Aws\\Exception\\AWSException $e) {\n    // fix IAM before rendering, not during a cancel emergency\n}","typeGuard":null,"tryCatchPattern":"use Remotion\\Lambda\\Exception\\Exception as RemotionException;\n\ntry {\n    $client->cancelRenderOnLambda($renderId, $bucketName);\n} catch (\\Throwable $e) {\n    $cause = $e->getPrevious();\n    if ($cause instanceof \\Aws\\Exception\\AWSException) {\n        $code = $cause->getAwsErrorCode();\n        if (in_array($code, ['SlowDown', 'RequestTimeout'], true)) { /* retry once */ }\n        if ($code === 'AccessDenied') { /* fix s3:PutObject on renders/* */ }\n    }\n    throw $e;\n}","preventionTips":["Run the PHP client with credentials that have s3:GetObject + s3:PutObject on the bucket's renders/* prefix - the same surface the render itself uses.","Take bucketName from the render response and reuse it for cancel; never retype it.","Always log getPrevious() of the wrapped exception so AWS root causes are not lost."],"tags":["lambda","s3","php","iam-permissions","cancellation","aws-sdk"],"backgroundTag":"s3-putobject-denied","analyzedSha":"10db9de07356446fb0edb3c3ae211369b693d18b","analyzedAt":"2026-08-22T21:45:17.748Z","contentChangedAt":"2026-08-22T21:45:17.748Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}