{"record":{"id":"e326da282137b46f","repo":"infiniflow/ragflow","slug":"azure-blob-credential-rejected-msg-300","errorCode":null,"errorMessage":"Azure Blob credential rejected: {msg[:300]}","messagePattern":"Azure Blob credential rejected: (.+?)","errorType":"validation","errorClass":"ConnectorMissingCredentialError","httpStatus":null,"severity":"error","filePath":"common/data_source/azure_blob_connector.py","lineNumber":191,"sourceCode":"\n    # ------------------------------------------------------------------\n    # Validation\n    # ------------------------------------------------------------------\n\n    def validate_connector_settings(self) -> None:\n        if self._container_client is None:\n            raise ConnectorMissingCredentialError(\"Azure Blob\")\n\n        try:\n            # get_container_properties() costs one API call; it returns\n            # the ETag and last-modified of the container, proving both\n            # the credential and the container name are valid.\n            self._container_client.get_container_properties()\n        except Exception as exc:\n            msg = str(exc)\n            code = getattr(getattr(exc, \"error_code\", None), \"value\", None) or getattr(exc, \"error_code\", \"\")\n            if \"AuthenticationFailed\" in msg or \"InvalidAuthenticationInfo\" in msg:\n                raise ConnectorMissingCredentialError(f\"Azure Blob credential rejected: {msg[:300]}\") from exc\n            if \"AuthorizationPermissionMismatch\" in msg or \"403\" in msg:\n                raise InsufficientPermissionsError(f\"Azure Blob: insufficient permissions on container: {msg[:300]}\") from exc\n            if \"ContainerNotFound\" in msg or \"404\" in msg:\n                raise ConnectorValidationError(f\"Azure Blob: container not found: {msg[:300]}\") from exc\n            raise UnexpectedValidationError(f\"Azure Blob validation failed ({code}): {msg[:300]}\") from exc\n\n    # ------------------------------------------------------------------\n    # Checkpoint helpers\n    # ------------------------------------------------------------------\n\n    def build_dummy_checkpoint(self) -> AzureBlobCheckpoint:\n        return AzureBlobCheckpoint(has_more=True)\n\n    def validate_checkpoint_json(self, checkpoint_json: str) -> AzureBlobCheckpoint:\n        try:\n            return AzureBlobCheckpoint.model_validate_json(checkpoint_json)\n        except Exception:\n            return self.build_dummy_checkpoint()","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/azure_blob_connector.py#L173-L209","documentation":"Raised by validate_connector_settings when the real Azure API call get_container_properties() fails with an authentication error. The connector maps Azure's 'AuthenticationFailed' or 'InvalidAuthenticationInfo' error strings to ConnectorMissingCredentialError, truncating the Azure message to 300 chars. This is the first error that proves the credential actually reached Azure and was rejected.","triggerScenarios":"load_credentials succeeded locally (well-formed key/SAS/connection string) but Azure rejected it during validation: wrong account key (rotated?), expired or wrongly-scoped SAS token, or a connection string whose key no longer matches the storage account.","commonSituations":"Storage account keys rotated after the credential was stored, SAS token expiry reached, SAS signed against the wrong account/container, or clock skew making SAS start-time in the future.","solutions":["Regenerate/re-copy the account key or SAS token from the Azure portal and update the stored credential","For SAS: check expiry and start time (skew), and that it is signed for the right account and container with the 'container' resource and 'read'+'list' permissions","For connection string: verify against the portal's Access keys blade — a rotated key2 invalidates old values","Retry validation after updating; if it still fails, decode the Azure error body (first 300 chars are included in the message)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"# No purely local pre-check can verify an Azure credential; the cheapest\n# pre-flight is the same call the connector makes:\nfrom datetime import datetime, timezone\nif sas_token:\n    se = re.search(r\"se=(\\d{4}-\\d{2}-\\d{2}T[\\d:%Z-]+)\", sas_token)\n    if se and datetime.now(timezone.utc) > datetime.fromisoformat(se.group(1).replace(\"Z\", \"+00:00\")):\n        raise ValueError(\"SAS token already expired\")","typeGuard":null,"tryCatchPattern":"try:\n    connector.validate_connector_settings()\nexcept ConnectorMissingCredentialError as e:\n    if \"credential rejected\" in str(e):\n        # credential is definitively bad — refresh it, do not blind-retry\n        creds = refresh_from_secret_store()\n        connector.load_credentials(creds)\n        connector.validate_connector_settings()\n    else:\n        raise","preventionTips":["Monitor SAS token expiry dates and rotate before they lapse","Treat 'credential rejected' as a hard failure: refresh the secret, never retry the same value","Store which key/version a credential came from so rejections after rotation are traceable"],"tags":["azure","azure-blob","authentication","sas-token","validation"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}