{"record":{"id":"7eeef52d2578e075","repo":"infiniflow/ragflow","slug":"moodle-token-is-invalid-or-expired","errorCode":null,"errorMessage":"Moodle token is invalid or expired","messagePattern":"Moodle token is invalid or expired","errorType":"validation","errorClass":"CredentialExpiredError","httpStatus":null,"severity":"error","filePath":"common/data_source/moodle_connector.py","lineNumber":80,"sourceCode":"    def _get_latest_timestamp(self, *timestamps: int) -> int:\n        \"\"\"Return latest valid timestamp\"\"\"\n        return max((t for t in timestamps if t and t > 0), default=0)\n\n    def _yield_in_batches(self, generator: Generator[Document, None, None]) -> Generator[list[Document], None, None]:\n        for batch in batch_generator(generator, self.batch_size):\n            yield batch\n\n    def load_credentials(self, credentials: dict[str, Any]) -> None:\n        token = credentials.get(\"moodle_token\")\n        if not token:\n            raise ConnectorMissingCredentialError(\"Moodle API token is required\")\n\n        try:\n            self.moodle_client = MoodleClient(self.moodle_url + \"/webservice/rest/server.php\", token)\n            self.moodle_client.core.webservice.get_site_info()\n        except MoodleException as e:\n            if \"invalidtoken\" in str(e).lower():\n                raise CredentialExpiredError(\"Moodle token is invalid or expired\")\n            raise ConnectorMissingCredentialError(f\"Failed to initialize Moodle client: {e}\")\n\n    def validate_connector_settings(self) -> None:\n        if not self.moodle_client:\n            raise ConnectorMissingCredentialError(\"Moodle client not initialized\")\n\n        try:\n            site_info = self.moodle_client.core.webservice.get_site_info()\n            if not site_info.sitename:\n                raise InsufficientPermissionsError(\"Invalid Moodle API response\")\n        except MoodleException as e:\n            msg = str(e).lower()\n            if \"invalidtoken\" in msg:\n                raise CredentialExpiredError(\"Moodle token is invalid or expired\")\n            if \"accessexception\" in msg:\n                raise InsufficientPermissionsError(\"Insufficient permissions. Ensure web services are enabled and permissions are correct.\")\n            raise ConnectorValidationError(f\"Moodle validation error: {e}\")\n        except Exception as e:","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/moodle_connector.py#L62-L98","documentation":"A CredentialExpiredError raised in load_credentials when constructing the MoodleClient and calling core.webservice.get_site_info() throws a MoodleException whose text contains 'invalidtoken'. The probe call doubles as credential verification; Moodle returns an invalidtoken error code when the token does not exist, was revoked, or expired.","triggerScenarios":"Calling load_credentials with a token that Moodle rejects during the get_site_info handshake — deleted/expired token, token for a different site, or a token whose user was removed.","commonSituations":"Token TTL elapsed (Moodle tokens can be configured to expire); admin pruned tokens; token copied from another Moodle instance; trailing whitespace/newline pasted with the token.","solutions":["Issue a fresh token in Moodle (Manage tokens) and update the stored credential.","Confirm the token belongs to the same Moodle site as moodle_url.","Strip whitespace from the token before storing it.","If tokens keep expiring, ask the Moodle admin to set a longer TTL or create a system-token for the service."],"exampleFix":"# before\nconnector.load_credentials({'moodle_token': old_token})  # CredentialExpiredError: Moodle token is invalid or expired\n\n# after\ntoken = os.environ['MOODLE_TOKEN'].strip()\nconnector.load_credentials({'moodle_token': token})","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    connector.load_credentials({'moodle_token': token})\nexcept CredentialExpiredError:\n    token = request_new_moodle_token()  # admin/API re-issue\n    connector.load_credentials({'moodle_token': token})","preventionTips":["Automate token rotation instead of hard-coding one token forever.","Strip whitespace and confirm the token belongs to the target site before first use."],"tags":["moodle","authentication","expired-token","credentials","connector"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}