{"record":{"id":"a1fe605954152a9c","repo":"infiniflow/ragflow","slug":"azure-blob","errorCode":null,"errorMessage":"Azure Blob","messagePattern":"Azure Blob","errorType":"validation","errorClass":"ConnectorMissingCredentialError","httpStatus":null,"severity":"error","filePath":"common/data_source/azure_blob_connector.py","lineNumber":180,"sourceCode":"                self._container_client = ContainerClient.from_container_url(full_url)\n            else:\n                raise ConnectorMissingCredentialError(\n                    \"Azure Blob credentials are incomplete. Provide one of: (a) connection_string + container_name, (b) account_name + account_key + container_name, (c) container_url + sas_token.\"\n                )\n        except ConnectorMissingCredentialError:\n            raise\n        except Exception as exc:\n            raise ConnectorMissingCredentialError(f\"Failed to initialise Azure Blob client: {exc}\") from exc\n\n        return None\n\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    # ------------------------------------------------------------------","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/azure_blob_connector.py#L162-L198","documentation":"A guard inside validate_connector_settings (and similarly in retrieve_all_slim_docs_perm_sync / _iter_documents) that fires when the stored _container_client is None, i.e. load_credentials never successfully ran or ran in a mode that left no client. The message is just 'Azure Blob', making it the least descriptive error in the connector — it means 'connector used before credentials were loaded'.","triggerScenarios":"Calling validate_connector_settings(), retrieve_all_slim_docs_perm_sync(), or _iter_documents() on a connector instance where load_credentials was never called, failed before assigning _container_client, or was skipped by an orchestration bug.","commonSituations":"A job runner that constructs the connector and immediately calls validate (assuming construction loads credentials), or a retry path that creates a fresh connector instance but only re-runs part of the setup.","solutions":["Ensure load_credentials(credentials) is called and succeeds before validate_connector_settings() or any document iteration","Check that load_credentials did not raise earlier — an earlier ConnectorMissingCredentialError was probably swallowed","If orchestrating, assert connector._container_client is not None after the load step in debug builds to pinpoint the ordering bug"],"exampleFix":"# before\nconnector = AzureBlobConnector(batch_size=100)\nconnector.validate_connector_settings()\n\n# after\nconnector = AzureBlobConnector(batch_size=100)\nconnector.load_credentials(creds)\nconnector.validate_connector_settings()","handlingStrategy":"validation","validationCode":"def ensure_loaded(connector) -> None:\n    connector.load_credentials(creds)\n    if connector._container_client is None:\n        raise RuntimeError(\"load_credentials returned but no client was built\")","typeGuard":null,"tryCatchPattern":"try:\n    connector.validate_connector_settings()\nexcept ConnectorMissingCredentialError:\n    # means 'not loaded yet' in this guard — fix the setup order, don't retry\n    connector.load_credentials(creds)\n    connector.validate_connector_settings()","preventionTips":["Standardize a build_connector(creds) factory that always runs load_credentials and returns a ready connector","Never let orchestration swallow a load_credentials failure and continue to validate/poll","Add an assertion after setup in dev builds: assert connector._container_client is not None"],"tags":["azure","azure-blob","lifecycle","initialization","connector"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}