{"record":{"id":"bd559187cb114880","repo":"laurent22/joplin","slug":"rejectedbytarget","errorCode":"rejectedByTarget","errorMessage":"Do not have proper permissions to Bucket","messagePattern":"Do not have proper permissions to Bucket","errorType":"exception","errorClass":"JoplinError","httpStatus":null,"severity":"error","filePath":"packages/lib/file-api-driver-amazon-s3.js","lineNumber":281,"sourceCode":"\t\t\t// This means that the error was on the Desktop client side and we need to handle that.\n\t\t\t// On Mobile it won't match because FetchError is a node-fetch feature.\n\t\t\t// https://github.com/node-fetch/node-fetch/blob/main/docs/ERROR-HANDLING.md\n\t\t\tif (error.name === 'FetchError') { throw error.message; }\n\n\t\t\tlet parsedOutput = '';\n\n\t\t\t// If error.output is not xml the last else case should\n\t\t\t// actually let us see the output of error.\n\t\t\tif (error.output) {\n\t\t\t\tparsedOutput = parser.parse(error.output);\n\t\t\t\tif (this.hasErrorCode_(parsedOutput.Error, 'AuthorizationHeaderMalformed')) {\n\t\t\t\t\tthrow error.output;\n\t\t\t\t}\n\n\t\t\t\tif (this.hasErrorCode_(parsedOutput.Error, 'NoSuchKey')) {\n\t\t\t\t\treturn null;\n\t\t\t\t} else if (this.hasErrorCode_(parsedOutput.Error, 'AccessDenied')) {\n\t\t\t\t\tthrow new JoplinError('Do not have proper permissions to Bucket', 'rejectedByTarget');\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (error.output) {\n\t\t\t\t\tthrow error.output;\n\t\t\t\t} else {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Don't need to make directories, S3 is key based storage.\n\tasync mkdir() {\n\t\treturn true;\n\t}\n\n\tasync put(path, content, options = null) {\n\t\tconst remotePath = this.makePath_(path);","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/lib/file-api-driver-amazon-s3.js#L263-L299","documentation":"A JoplinError (code 'rejectedByTarget') thrown in the S3 driver's read/get path when the server returns an AccessDenied error parsed from the XML response body. It signals the configured AWS credentials lack read (GetObject/list) permission on the target bucket. Upstream sync code treats 'rejectedByTarget' specially — the item is skipped and logged rather than marked 'cannotSyncItem'.","triggerScenarios":"The S3 driver performs a GET-like operation (read of an object); AWS returns an XML error containing <Code>AccessDenied</Code>; the driver parses it and throws this. The AWS credentials in the sync config have permission to reach the bucket but not to read objects.","commonSituations":"IAM user/role with write but not read permissions on the bucket; bucket policy denies the principal; KMS key missing decrypt permission for SSE-encrypted objects; wrong bucket region/endpoint causing auth mismatch; credentials rotated to a lesser role.","solutions":["Update the IAM policy for the configured credentials to grant s3:GetObject (and s3:ListBucket if listing fails) on the bucket.","If using SSE-KMS, ensure kms:Decrypt permission on the encrypting key.","Verify the bucket policy explicitly allows the principal and doesn't have an explicit Deny.","Confirm the bucket name and region in the sync config are correct."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before configuring S3 sync, validate credentials have read access\n// (best done via a preflight HEAD/GetObject on a known key)\ntry {\n  await s3.send(new HeadObjectCommand({ Bucket, Key: '.joplin' }));\n} catch (e) {\n  // credentials lack read permission; fix IAM before syncing\n}","typeGuard":"import JoplinError from './JoplinError';\nfunction isRejectedByTarget(e: unknown): e is JoplinError {\n  return e instanceof JoplinError && (e as any).code === 'rejectedByTarget';\n}","tryCatchPattern":"try {\n  await driver.get(path);\n} catch (error) {\n  if (isRejectedByTarget(error)) {\n    // skip this item; log a warning; do not mark as cannotSyncItem\n    logger.warn('Item rejected by target', path, error.message);\n    return;\n  }\n  throw error;\n}","preventionTips":["Grant s3:GetObject (and s3:ListBucket) to the IAM principal used for sync.","If using SSE-KMS, also grant kms:Decrypt on the bucket's key.","Treat 'rejectedByTarget' as a per-item skip, not a fatal sync error."],"tags":["s3","permissions","iam","sync","aws","rejectedbytarget"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}