{"record":{"id":"3becfb38478c181a","repo":"remotion-dev/remotion","slug":"unable-to-access-item-objectkey-from-bucket","errorCode":null,"errorMessage":"Unable to access item \"${objectKey}\" from bucket \"${bucketName}\". You must have permission for both \"s3:GetObject\" and \"s3:ListBucket\" actions.","messagePattern":"Unable to access item \"(.+?)\" from bucket \"(.+?)\"\\. You must have permission for both \"s3:GetObject\" and \"s3:ListBucket\" actions\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/lambda-client/src/presign-url.ts","lineNumber":76,"sourceCode":"\tif (checkIfObjectExists === true) {\n\t\ttry {\n\t\t\tawait s3Client.send(\n\t\t\t\tnew HeadObjectCommand({\n\t\t\t\t\tBucket: bucketName,\n\t\t\t\t\tKey: objectKey,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tif ((err as {name: string}).name === 'NotFound') {\n\t\t\t\treturn null as unknown as string;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t(err as Error).message === 'UnknownError' ||\n\t\t\t\t(err as {$metadata: {httpStatusCode: number}}).$metadata\n\t\t\t\t\t.httpStatusCode === 403\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Unable to access item \"${objectKey}\" from bucket \"${bucketName}\". You must have permission for both \"s3:GetObject\" and \"s3:ListBucket\" actions.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tconst objCommand = new GetObjectCommand({\n\t\tBucket: bucketName,\n\t\tKey: objectKey,\n\t});\n\n\tconst publicUrl = await getSignedUrl(s3Client, objCommand, {\n\t\texpiresIn: expiresInSeconds,\n\t});\n\n\treturn publicUrl;","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/lambda-client/src/presign-url.ts#L58-L94","documentation":"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.","triggerScenarios":"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.)","commonSituations":"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.","solutions":["Grant both s3:GetObject on the object ARN and s3:ListBucket on the bucket ARN to the IAM principal.","If using a bucket policy, ensure ListBucket covers the prefix of objectKey.","Confirm the credentials in use belong to the account that owns the bucket.","Check CloudTrail/S3 access logs for the underlying 403 reason if the policy looks correct."],"exampleFix":"// before: only GetObject\n// { Effect: 'Allow', Action: ['s3:GetObject'], Resource: 'arn:aws:s3:::bucket/*' }\n\n// after: both required actions\n// [\n//   { Effect: 'Allow', Action: ['s3:GetObject'], Resource: 'arn:aws:s3:::bucket/*' },\n//   { Effect: 'Allow', Action: ['s3:ListBucket'], Resource: 'arn:aws:s3:::bucket' }\n// ]","handlingStrategy":"validation","validationCode":"// Pre-check that the role can both GetObject and ListBucket before presigning\nawait s3.send(new GetObjectCommand({ Bucket: bucketName, Key: objectKey }));\nawait s3.send(new ListObjectsV2Command({ Bucket: bucketName, Prefix: prefix, MaxKeys: 1 }));","typeGuard":null,"tryCatchPattern":"try {\n  const url = await presignUrl({ bucketName, objectKey, ... });\n} catch (e) {\n  if (e instanceof Error && /s3:GetObject.*s3:ListBucket/.test(e.message)) {\n    // alert ops to fix IAM; do not retry until policy updated\n  }\n  throw e;\n}","preventionTips":["Always grant s3:GetObject on object ARNs AND s3:ListBucket on the bucket ARN together.","For presigned URLs, ensure ListBucket covers the object's prefix.","Validate IAM with a dry-run GetObject before relying on presigning in production."],"tags":["aws","s3","iam","presigned-url","permissions"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}