infiniflow/ragflow · error · Error
main() must be defined or exported.
Error message
main() must be defined or exported.
What it means
Thrown from validate_connector_settings when python-gitlab's auth() or projects.get() raises GitlabAuthenticationError. The GitLab Personal Access Token (PAT) is invalid, revoked, or the account it belonged to no longer authenticates. It maps to CredentialExpiredError so the backend can prompt for credential refresh.
Source
Thrown at agent/sandbox/result_protocol.py:46
if __name__ == "__main__":
import base64
import json
result = main(**{args_json})
payload = json.dumps({{"present": True, "value": result, "type": "json"}}, ensure_ascii=False, separators=(",", ":"))
print("{RESULT_MARKER_PREFIX}" + base64.b64encode(payload.encode("utf-8")).decode("ascii"))
'''
def build_javascript_wrapper(code: str, args_json: str) -> str:
return f"""{code}
const __ragflowArgs = {args_json};
(async () => {{
const __ragflowMain = typeof main !== 'undefined' ? main : module.exports && module.exports.main;
if (typeof __ragflowMain !== 'function') {{
throw new Error('main() must be defined or exported.');
}}
const output = await Promise.resolve(__ragflowMain(__ragflowArgs));
if (typeof output === 'undefined') {{
throw new Error('main() must return a value. Use null for an empty result.');
}}
const payload = JSON.stringify({{ present: true, value: output, type: 'json' }});
if (typeof payload === 'undefined') {{
throw new Error('main() returned a non-JSON-serializable value.');
}}
console.log('{RESULT_MARKER_PREFIX}' + Buffer.from(payload, 'utf8').toString('base64'));
}})();
"""
def extract_structured_result(stdout: str) -> tuple[str, dict[str, Any]]:
if not stdout:
return "", {}
View on GitHub (pinned to 554fb1133a)
Solutions
- Verify the PAT still works: curl -H 'PRIVATE-TOKEN: <token>' https://gitlab.example.com/api/v4/user — 401 means regenerate the token
- Regenerate the PAT in GitLab (User Settings > Access Tokens) with api/read_api + read_repository scopes and re-enter it in the connector credential
- If OAuth-based, disconnect and redo the OAuth flow to mint a fresh refresh token
- Confirm the token owner account is active and not blocked/ deactivated
Example fix
# before: token stored with whitespace / expired
credentials = {"gitlab_access_token": " glpat-XXXX \n"}
# after: trim and validate before saving
import re
token = credentials["gitlab_access_token"].strip()
if not re.fullmatch(r"glpat-[A-Za-z0-9_-]{20,}", token):
raise ValueError("Malformed GitLab PAT — expected glpat-...")
credentials["gitlab_access_token"] = token Defensive patterns
Strategy: try-catch
Validate before calling
import requests
def gitlab_token_alive(base_url: str, token: str) -> bool:
r = requests.get(
f"{base_url.rstrip('/')}/api/v4/user",
headers={"PRIVATE-TOKEN": token},
timeout=10,
)
return r.status_code == 200 Type guard
def is_valid_gitlab_credential_payload(creds: dict) -> bool:
tok = creds.get("gitlab_access_token", "")
return bool(tok) and tok == tok.strip() and tok.startswith("glpat-") Try / catch
from common.data_source.exceptions import CredentialExpiredError
try:
connector.validate_connector_settings()
except CredentialExpiredError:
# prompt user to re-enter token, then retry once
connector.load_credentials(prompt_for_new_token())
connector.validate_connector_settings() Prevention
- Set a calendar for PAT expiry (gitlab.com free tokens expire in 90 days) and rotate proactively
- Trim and format-check tokens before persisting them
- Run validate_connector_settings() right after saving credentials so expiry surfaces at config time, not mid-index
When it happens
Trigger: Calling validate_connector_settings() (typically from the connector-credential update UI or a test-connection endpoint) when the stored PAT is expired/revoked, was pasted with whitespace or a typo, or belongs to a deleted/deactivated user. Also occurs with OAuth tokens whose refresh failed.
Common situations: Free-tier PAT expiry (90-day for gitlab.com), admin rotating tokens without updating the connector credential, tokens copied from a password manager with trailing newlines, SSO-enforced accounts with personal tokens disabled.
Related errors
- Invalid chat_id: ${payload.chat_id}
- main() must be defined or exported.
- Bitbucket
- Provided blob storage credentials appear invalid or expired.
- GitLab
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/eb02c150a6330015.
Report an issue: GitHub.