infiniflow/ragflow · error · ConnectorValidationError
Failed to fetch courses: {e}
Error message
Failed to fetch courses: {e} What it means
A ConnectorValidationError raised in _get_enrolled_courses when core.course.get_courses() throws a MoodleException. The raw Moodle error text is embedded, and because the helper is wrapped in @retry(tries=3, delay=1, backoff=2) Moodle-side errors are retried three times before this surfaces. Unlike its sibling _get_course_contents (which swallows errors and returns []), the course fetch is treated as fatal — without courses there is nothing to index.
Source
Thrown at common/data_source/moodle_connector.py:210
f"slim snapshot for course {getattr(course, 'fullname', '?')}",
e,
)
if batch:
yield batch
logger.info(f"Moodle slim snapshot completed: {total} documents listed")
@retry(tries=3, delay=1, backoff=2)
def _get_enrolled_courses(self) -> list:
if not self.moodle_client:
raise ConnectorMissingCredentialError("Moodle client not initialized")
try:
return self.moodle_client.core.course.get_courses()
except MoodleException as e:
self._log_error("fetching courses", e, "error")
raise ConnectorValidationError(f"Failed to fetch courses: {e}")
@retry(tries=3, delay=1, backoff=2)
def _get_course_contents(self, course_id: int):
if not self.moodle_client:
raise ConnectorMissingCredentialError("Moodle client not initialized")
try:
return self.moodle_client.core.course.get_contents(courseid=course_id)
except MoodleException as e:
self._log_error(f"fetching course contents for {course_id}", e)
return []
def _process_courses(self, courses) -> Generator[Document, None, None]:
for course in courses:
try:
contents = self._get_course_contents(course.id)
for section in contents:
for module in section.modules:View on GitHub (pinned to 554fb1133a)
Solutions
- Read the embedded {e} text — Moodle error codes (invalidtoken, accessexception, missing capability, invalidfunction) each point to a distinct fix.
- Ensure the token's service includes core_course_get_courses (and core_course_get_contents for later steps), or use an admin-issued token.
- Grant the token user the moodle/course:view capability on the courses to index.
- Verify directly: curl '<moodle_url>/webservice/rest/server.php?wstoken=TOKEN&wsfunction=core_course_get_courses&moodlewsrestformat=json'.
Example fix
# before
connector.load_credentials(creds)
for batch in connector.load_from_state(): # ConnectorValidationError('Failed to fetch courses: ...')
...
# after
# fix token service scope in Moodle (include core_course_get_courses), then
connector.load_credentials(new_creds)
import requests
r = requests.get(f"{moodle_url}/webservice/rest/server.php", params={"wstoken": token, "wsfunction": "core_course_get_courses", "moodlewsrestformat": "json"})
r.raise_for_status() # confirm no exception block in response before re-running the load Defensive patterns
Strategy: retry
Validate before calling
import requests
def can_fetch_courses(moodle_url: str, token: str) -> bool:
r = requests.get(
f"{moodle_url}/webservice/rest/server.php",
params={"wstoken": token, "wsfunction": "core_course_get_courses", "moodlewsrestformat": "json"},
timeout=10,
)
data = r.json()
return "exception" not in data Try / catch
try:
for batch in connector.load_from_state():
process(batch)
except ConnectorValidationError as exc:
if not str(exc).startswith("Failed to fetch courses"):
raise
msg = str(exc).lower()
if "invalidtoken" in msg:
refresh_moodle_credentials(connector)
elif "accessexception" in msg or "capability" in msg:
notify_admin_to_grant_course_access()
else:
raise Prevention
- Create the Moodle token on a service that includes core_course_get_courses and core_course_get_contents.
- Pre-flight the course-list call once before starting a full load.
- Distinguish this fatal course-fetch error from the sibling _get_course_contents, which silently returns [] — check logs accordingly.
When it happens
Trigger: Calling any load path when Moodle rejects core_course_get_courses — token's service lacks that function, user lacks capability to view courses, or Moodle returns an error like 'accessexception'/'invalidtoken' at course-list time.
Common situations: Token created on a custom service that did not include core_course_get_courses; user role can authenticate but not list courses; Moodle upgrade that renamed/removed the function; debugging confusion from the triple retry in logs.
Related errors
- Insufficient permissions. Ensure web services are enabled an
- main() must return a value. Use null for an empty result.
- Failed to fetch memory list
- not implement: show role {role_name}
- not implement: grant role {role_name} actions: {actions} on
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/e8b2c5235b49039d.
Report an issue: GitHub.