remotion-dev/remotion · error

could not read progress for render %q: %w

Error message

could not read progress for render %q: %w

What it means

Go client error from cancelRenderOnLambda: the initial S3 GetObject for `renders/<renderId>/progress.json` failed, and the underlying S3 error (wrapped with %w) explains why — most often NoSuchKey because no render with that ID ever wrote progress to this bucket.

Source

Thrown at packages/lambda-go/s3.go:86

func inputPropsKey(hash string) string {
	return fmt.Sprintf("input-props/%s.json", hash)
}

func overallProgressKey(renderId string) string {
	return fmt.Sprintf("renders/%s/progress.json", renderId)
}

func cancellationKey(renderId string) string {
	return fmt.Sprintf("renders/%s/cancel.json", renderId)
}

func cancelRenderOnLambda(client cancellationObjectClient, input CancelRenderOnLambdaInput) error {
	progressObject, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
		Bucket: new(input.BucketName),
		Key:    new(overallProgressKey(input.RenderId)),
	})
	if err != nil {
		return fmt.Errorf("could not read progress for render %q: %w", input.RenderId, err)
	}
	defer progressObject.Body.Close()

	progressBody, err := io.ReadAll(progressObject.Body)
	if err != nil {
		return fmt.Errorf("could not read progress for render %q: %w", input.RenderId, err)
	}

	var progress struct {
		CancellationEnabled bool `json:"cancellationEnabled"`
	}
	if err := json.Unmarshal(progressBody, &progress); err != nil {
		return fmt.Errorf("could not parse progress for render %q: %w", input.RenderId, err)
	}
	if !progress.CancellationEnabled {
		return fmt.Errorf("cannot cancel render %s: the render was not started with enableCancellation: true", input.RenderId)
	}

View on GitHub (pinned to 10db9de073)

Solutions

  1. Unwrap the error (errors.Is/As on the smithy APIError) to distinguish NoSuchKey (wrong renderId/bucket) from AccessDenied (IAM).
  2. Verify RenderId matches the one returned by StartRenderOnLambda and BucketName is the same bucket the render wrote to.
  3. For AccessDenied, grant the caller s3:GetObject on the bucket's renders/* prefix.
  4. If the render already finished, treat cancellation as a no-op success in your orchestration code.

Example fix

 // before
err := client.CancelRenderOnLambda(ctx, input)

// after: distinguish missing progress from other failures
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "NoSuchKey" {
    // render not found in this bucket: nothing to cancel
    return nil
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the progress object exists before cancelling
_, err := client.HeadObject(ctx, &s3.HeadObjectInput{
  Bucket: ptr(bucketName),
  Key:    ptr(fmt.Sprintf("renders/%s/progress.json", renderId)),
})
if err != nil { /* render unknown/finished: skip cancel */ }

Type guard

var notFound *types.NoSuchKey
isRenderProgressMissing := func(err error) bool {
  var apiErr smithy.APIError
  return errors.As(err, &apiErr) && apiErr.ErrorCode() == "NoSuchKey"
}

Try / catch

if err := client.CancelRenderOnLambda(ctx, input); err != nil {
  var apiErr smithy.APIError
  if errors.As(err, &apiErr) && apiErr.ErrorCode() == "NoSuchKey" {
    // no such render in this bucket: treat as already finished
    return nil
  }
  return fmt.Errorf("cancel render: %w", err)
}

Prevention

When it happens

Trigger: CancelRenderOnLambda with a wrong or mistyped RenderId, a render in a different bucket (BucketName mismatch), or an S3/permissions problem on GetObject (AccessDenied is wrapped the same way).

Common situations: Persisting render IDs and later cancelling against a different environment/bucket; render finished and its files were already cleaned up; wrong region endpoint or credentials.

Related errors


AI-assisted analysis of remotion-dev/remotion@10db9de073 (2026-08-22). Data as JSON: /api/errors/e4050064addd4a37. Report an issue: GitHub.