remotion-dev/remotion · error · Error

Unable to access item "${objectKey}" from bucket "${bucketNa

Error message

Unable to access item "${objectKey}" from bucket "${bucketName}". You must have permission for both "s3:GetObject" and "s3:ListBucket" actions.

What it means

Thrown by presign-url when generating a pre-signed GET URL fails with an unknown error or HTTP 403. S3 returns 403 for missing permissions; a 403 on presigning indicates the credentials are not allowed both s3:GetObject (for the object) and s3:ListBucket (on the bucket) — AWS requires both to validate the object's existence for a presigned download.

Source

Thrown at packages/lambda-client/src/presign-url.ts:76

	if (checkIfObjectExists === true) {
		try {
			await s3Client.send(
				new HeadObjectCommand({
					Bucket: bucketName,
					Key: objectKey,
				}),
			);
		} catch (err) {
			if ((err as {name: string}).name === 'NotFound') {
				return null as unknown as string;
			}

			if (
				(err as Error).message === 'UnknownError' ||
				(err as {$metadata: {httpStatusCode: number}}).$metadata
					.httpStatusCode === 403
			) {
				throw new Error(
					`Unable to access item "${objectKey}" from bucket "${bucketName}". You must have permission for both "s3:GetObject" and "s3:ListBucket" actions.`,
				);
			}

			throw err;
		}
	}

	const objCommand = new GetObjectCommand({
		Bucket: bucketName,
		Key: objectKey,
	});

	const publicUrl = await getSignedUrl(s3Client, objCommand, {
		expiresIn: expiresInSeconds,
	});

	return publicUrl;

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Grant both s3:GetObject on the object ARN and s3:ListBucket on the bucket ARN to the IAM principal.
  2. If using a bucket policy, ensure ListBucket covers the prefix of objectKey.
  3. Confirm the credentials in use belong to the account that owns the bucket.
  4. Check CloudTrail/S3 access logs for the underlying 403 reason if the policy looks correct.

Example fix

// before: only GetObject
// { Effect: 'Allow', Action: ['s3:GetObject'], Resource: 'arn:aws:s3:::bucket/*' }

// after: both required actions
// [
//   { Effect: 'Allow', Action: ['s3:GetObject'], Resource: 'arn:aws:s3:::bucket/*' },
//   { Effect: 'Allow', Action: ['s3:ListBucket'], Resource: 'arn:aws:s3:::bucket' }
// ]
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check that the role can both GetObject and ListBucket before presigning
await s3.send(new GetObjectCommand({ Bucket: bucketName, Key: objectKey }));
await s3.send(new ListObjectsV2Command({ Bucket: bucketName, Prefix: prefix, MaxKeys: 1 }));

Try / catch

try {
  const url = await presignUrl({ bucketName, objectKey, ... });
} catch (e) {
  if (e instanceof Error && /s3:GetObject.*s3:ListBucket/.test(e.message)) {
    // alert ops to fix IAM; do not retry until policy updated
  }
  throw e;
}

Prevention

When it happens

Trigger: The presigning HEAD/getObject probe catches an error whose message is 'UnknownError' or whose $metadata.httpStatusCode is 403, and re-throws this guidance message. (A NotFound error instead returns null.)

Common situations: Rendering with a service role whose policy only grants s3:GetObject but not s3:ListBucket; bucket policy restricting ListBucket to specific prefixes; using credentials from a different account; encryption/KMS misconfiguration surfacing as UnknownError.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/3becfb38478c181a. Report an issue: GitHub.