home-assistant/core · error · ConfigEntryError
invalid_credentials
invalid_credentials
Error message
Bucket cannot be accessed using provided combination of access key ID and secret access key.
What it means
Raised as ConfigEntryError (translation_key invalid_credentials) by the aws_s3 integration when session.create_client(...).head_bucket(Bucket=...) raises botocore ClientError — AWS rejected the access key ID / secret pair, or the key has no permission on the bucket. ConfigEntryError marks the entry as errored (no automatic retry) and surfaces a translated message to the user.
Source
Thrown at homeassistant/components/aws_s3/__init__.py:43
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: S3ConfigEntry) -> bool:
"""Set up S3 from a config entry."""
data = cast(dict, entry.data)
try:
session = AioSession()
# pylint: disable-next=unnecessary-dunder-call
client = await session.create_client(
"s3",
endpoint_url=data.get(CONF_ENDPOINT_URL),
aws_secret_access_key=data[CONF_SECRET_ACCESS_KEY],
aws_access_key_id=data[CONF_ACCESS_KEY_ID],
).__aenter__()
await client.head_bucket(Bucket=data[CONF_BUCKET])
except ClientError as err:
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_credentials",
) from err
except ParamValidationError as err:
if "Invalid bucket name" in str(err):
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_bucket_name",
) from err
except ValueError as err:
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_endpoint_url",
) from err
except ConnectionError as err:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="cannot_connect",View on GitHub (pinned to 58a3fdb3ea)
Solutions
- Re-enter the access key ID and secret in the aws_s3 config entry (reconfigure/re-auth flow) and verify they are exact, with no trailing whitespace.
- Test the same keys with aws s3api head-bucket --bucket <name> — if that also fails, fix IAM.
- Grant the IAM principal s3:ListBucket (and s3:GetBucketLocation) on the bucket in its policy.
- If the key belongs to another account, add a bucket policy allowing it, or use keys from the bucket-owning account.
Defensive patterns
Strategy: validation
Validate before calling
import re
def keys_well_formed(access_key_id: str, secret: str) -> bool:
return bool(re.fullmatch(r"[A-Z0-9]{16,128}", access_key_id or "")) and len(secret or "") >= 16
Try / catch
from botocore.exceptions import ClientError
try:
await client.head_bucket(Bucket=bucket)
except ClientError as err:
code = err.response["Error"]["Code"]
if code in ("InvalidAccessKeyId", "SignatureDoesNotMatch", "AccessDenied"):
# credentials/permission problem — do not retry
raise Prevention
- Verify keys with aws s3api head-bucket before configuring the integration.
- Grant s3:ListBucket on the exact bucket ARN in the IAM policy.
- Re-enter credentials on rotation; avoid shared keys across services.
When it happens
Trigger: head_bucket with the configured access key ID and secret returns 403 InvalidAccessKeyId / SignatureDoesNotMatch / AccessDenied: wrong keys, deleted IAM user, or an IAM policy lacking s3:ListBucket/GetBucketLocation on the target bucket.
Common situations: Typo pasting keys; rotated/deleted access keys; IAM policy missing s3:HeadBucket permission; keys from a different AWS account than the bucket owner.
Related errors
- invalid_bucket_name
- invalid_endpoint_url
- cannot_connect
- Failed during {func.__name__}
- Failed to upload backup
AI-assisted analysis of home-assistant/core@58a3fdb3ea (2026-08-14).
Data as JSON: /api/errors/07f06bcfe4d0609a.
Report an issue: GitHub.