{"record":{"id":"1d8910f3a3b8ffcb","repo":"infiniflow/ragflow","slug":"azure-blob-container-url-and-sas-token-are-requir","errorCode":null,"errorMessage":"Azure Blob: container_url and sas_token are required for the sas_token auth mode","messagePattern":"Azure Blob: container_url and sas_token are required for the sas_token auth mode","errorType":"validation","errorClass":"ConnectorMissingCredentialError","httpStatus":null,"severity":"error","filePath":"common/data_source/azure_blob_connector.py","lineNumber":157,"sourceCode":"                    raise ConnectorMissingCredentialError(\"Azure Blob: connection_string is required for the connection_string auth mode\")\n                if not container_name:\n                    raise ConnectorMissingCredentialError(\"Azure Blob: container_name is required together with connection_string\")\n                svc = BlobServiceClient.from_connection_string(conn_str)\n                self._container_client = svc.get_container_client(container_name)\n            elif mode == \"account_key\":\n                if not (account_name and account_key):\n                    raise ConnectorMissingCredentialError(\"Azure Blob: account_name and account_key are required for the account_key auth mode\")\n                if not container_name:\n                    raise ConnectorMissingCredentialError(\"Azure Blob: container_name is required together with account_name + account_key\")\n                account_url = f\"https://{account_name}.{_AZURE_ENDPOINT_SUFFIX}\"\n                svc = BlobServiceClient(\n                    account_url=account_url,\n                    credential=account_key,\n                )\n                self._container_client = svc.get_container_client(container_name)\n            elif mode == \"sas_token\":\n                if not (container_url and sas_token):\n                    raise ConnectorMissingCredentialError(\"Azure Blob: container_url and sas_token are required for the sas_token auth mode\")\n                # mirrors RAGFlowAzureSasBlob; strip a leading \"?\" so we\n                # never produce a double-\"?\" that breaks SAS auth.\n                normalized_sas = str(sas_token).lstrip(\"?\")\n                full_url = f\"{container_url}?{normalized_sas}\"\n                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","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/azure_blob_connector.py#L139-L175","documentation":"Raised when the Azure Blob connector is configured for sas_token auth mode but either container_url or sas_token is missing from the credentials. The SAS flow constructs a full URL as f\"{container_url}?{normalized_sas}\" and hands it to ContainerClient.from_container_url, so both halves are mandatory. It is a local validation failure — no Azure request is made.","triggerScenarios":"load_credentials receives credentials containing a sas_token or container_url key that implies the sas_token branch (per the mode resolution logic earlier in load_credentials), while the other of the pair is absent, empty, or None.","commonSituations":"Copying only the SAS token from Azure Storage Explorer and forgetting the container URL, pasting the full URL (token included) into container_url only, or a secrets manager that stores the two values under different paths and only one was wired into the connector config.","solutions":["Set both 'container_url' (e.g. https://account.blob.core.windows.net/container) and 'sas_token' in the credentials dict","If you pasted a URL that already contains the '?sig=...' query, split it: base URL into container_url, query part into sas_token (a leading '?' is stripped automatically)","If you meant key-based auth, supply account_name + account_key + container_name instead"],"exampleFix":"// before\ncreds = {\"sas_token\": \"sv=2022-11-02&sig=...\"}\nconnector.load_credentials(creds)\n\n// after\ncreds = {\n  \"container_url\": \"https://myaccount.blob.core.windows.net/my-container\",\n  \"sas_token\": \"sv=2022-11-02&sig=...\",\n}\nconnector.load_credentials(creds)","handlingStrategy":"validation","validationCode":"def validate_azure_sas_creds(creds: dict) -> None:\n    url = creds.get(\"container_url\") or \"\"\n    token = creds.get(\"sas_token\") or \"\"\n    if not (url.startswith(\"https://\") and token):\n        raise ValueError(\"sas_token mode requires container_url and sas_token\")\n    if \"sig=\" in url:\n        raise ValueError(\"container_url already contains a SAS query; split it\")","typeGuard":"def is_complete_sas_creds(c: dict) -> bool:\n    return bool(c.get(\"container_url\", \"\").startswith(\"https://\") and c.get(\"sas_token\"))","tryCatchPattern":"try:\n    connector.load_credentials(creds)\nexcept ConnectorMissingCredentialError as e:\n    raise ConfigError(str(e)) from e","preventionTips":["Never paste the signed URL into one field; store base URL and token separately","Reject a container_url that contains '?sig=' up front — it almost always means a mis-split SAS URL","Document the expected credential shape next to every UI form that creates it"],"tags":["azure","azure-blob","sas-token","credentials","connector"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}