{"record":{"id":"2f51f12faa097036","repo":"remotion-dev/remotion","slug":"could-not-cancel-render-q-w","errorCode":null,"errorMessage":"could not cancel render %q: %w","messagePattern":"could not cancel render %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/lambda-go/s3.go","lineNumber":116,"sourceCode":"\tif err := json.Unmarshal(progressBody, &progress); err != nil {\n\t\treturn fmt.Errorf(\"could not parse progress for render %q: %w\", input.RenderId, err)\n\t}\n\tif !progress.CancellationEnabled {\n\t\treturn fmt.Errorf(\"cannot cancel render %s: the render was not started with enableCancellation: true\", input.RenderId)\n\t}\n\n\tcancellationBody, err := json.Marshal(map[string]int64{\"cancelledAt\": time.Now().UnixMilli()})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not serialize cancellation signal: %w\", err)\n\t}\n\t_, err = client.PutObject(context.TODO(), &s3.PutObjectInput{\n\t\tBucket:      new(input.BucketName),\n\t\tKey:         new(cancellationKey(input.RenderId)),\n\t\tBody:        strings.NewReader(string(cancellationBody)),\n\t\tContentType: new(\"application/json\"),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not cancel render %q: %w\", input.RenderId, err)\n\t}\n\n\treturn nil\n}\n\n// newS3Client creates an S3 client using the same shared config resolution as\n// the Lambda client in invocations.go.\nfunc newS3Client(region string, forcePathStyle bool) (*s3.Client, error) {\n\tawsConfig, err := config.LoadDefaultConfig(\n\t\tcontext.TODO(),\n\t\tconfig.WithRegion(region),\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not load AWS config: %w\", err)\n\t}\n\treturn s3.NewFromConfig(awsConfig, func(options *s3.Options) {\n\t\toptions.UsePathStyle = forcePathStyle\n\t}), nil","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/remotion-dev/remotion/blob/10db9de07356446fb0edb3c3ae211369b693d18b/packages/lambda-go/s3.go#L98-L134","documentation":"Go client error from cancelRenderOnLambda: the final PutObject that writes the cancellation signal (renders/<renderId>/cancel.json with a cancelledAt timestamp) failed; the S3 error is wrapped with %w. The read path succeeded, so this points at write permissions, bucket state, or transport issues.","triggerScenarios":"The caller has s3:GetObject but not s3:PutObject on the bucket; bucket deleted mid-flight; network failure during upload; wrong region endpoint for the bucket.","commonSituations":"IAM policies that only grant read for monitoring; least-privilege service roles missing the write statement for renders/*; S3 outage or throttling.","solutions":["Unwrap the error to the smithy APIError — AccessDenied means adding s3:PutObject on renders/* to the caller's policy.","Verify the bucket still exists and the region/endpoint in the client matches the bucket's region.","Retry once for transient throttling (SlowDown) with backoff."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isS3WriteDenied(err error) bool {\n  var apiErr smithy.APIError\n  return errors.As(err, &apiErr) && apiErr.ErrorCode() == \"AccessDenied\"\n}","tryCatchPattern":"if err := client.CancelRenderOnLambda(ctx, input); err != nil {\n  var apiErr smithy.APIError\n  if errors.As(err, &apiErr) {\n    switch apiErr.ErrorCode() {\n    case \"AccessDenied\": // fix IAM: add s3:PutObject on renders/*\n    case \"SlowDown\": // retry with backoff\n    default: return err\n    }\n  }\n}","preventionTips":["Grant cancelling roles both s3:GetObject and s3:PutObject on renders/* in the render bucket.","Verify bucket region matches the client's configured region before operations.","Retry PutObject-path failures once with backoff for throttling."],"tags":["go","s3","permissions","cancellation"],"backgroundTag":"s3-write-failed","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"}