infiniflow/ragflow · error · Error
main() returned a non-JSON-serializable value.
Error message
main() returned a non-JSON-serializable value.
What it means
load_credentials expects the credential JSON to contain the primary-admin key (DB_CREDENTIALS_PRIMARY_ADMIN_KEY). Its absence raises this ValueError, aborting credential loading. The primary admin email is the identity used for delegated Drive access and is mandatory regardless of OAuth vs service-account auth.
Source
Thrown at internal/agent/sandbox/result_protocol.go:117
// template-literal escapes only on the JS side. We pass them
// through as-is.
return code + `
const __ragflowArgsB64 = "` + argsB64 + `";
const __ragflowArgs = JSON.parse(Buffer.from(__ragflowArgsB64, 'base64').toString('utf8'));
(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('` + resultMarkerPrefix + `' + Buffer.from(payload, 'utf8').toString('base64'));
})();
`
}
// ExtractStructuredResult scans stdout for the marker line, decodes
// the JSON payload after it, and returns the user-visible stdout
// (with the marker line removed) plus the parsed structured result.
//
// The Python side returns `(cleaned_stdout, structured_result_dict)`.
// On Go the dict is `map[string]any`.
//
// Edge cases (matching the Python implementation):
// - empty stdout → ("", empty map).
// - multiple marker lines → only the LAST one wins (later result
// overrides earlier). The Python implementation does the same
// because the loop overwrites `structured_result`.View on GitHub (pinned to 554fb1133a)
Solutions
- Find the expected key name (DB_CREDENTIALS_PRIMARY_ADMIN_KEY, typically 'primary_admin_email') and add it to the credential JSON with the Workspace admin's email
- Use the UI's credential flow rather than hand-crafting JSON so the key is populated
- If migrating, re-run the credential creation step instead of copying partial payloads
Example fix
# before
credentials = {"google_drive_tokens": {"refresh_token": "..."}}
connector.load_credentials(credentials) # ValueError
# after
credentials = {
"google_drive_tokens": {"refresh_token": "..."},
"primary_admin_email": "admin@acme.com",
}
connector.load_credentials(credentials) Defensive patterns
Strategy: validation
Validate before calling
PRIMARY_ADMIN_KEY = "primary_admin_email" # DB_CREDENTIALS_PRIMARY_ADMIN_KEY
def credential_has_admin_key(creds: dict) -> bool:
return PRIMARY_ADMIN_KEY in creds and "@" in str(creds[PRIMARY_ADMIN_KEY]) Type guard
from typing import TypedDict
class GoogleDriveCreds(TypedDict, total=False):
google_drive_tokens: str
google_drive_service_account_key: str
primary_admin_email: str # required
def is_complete_gdrive_creds(d: dict) -> bool:
return "primary_admin_email" in d and (
"google_drive_tokens" in d or "google_drive_service_account_key" in d
) Try / catch
try:
connector.load_credentials(raw)
except ValueError as e:
if "primary admin key" in str(e):
raw.setdefault("primary_admin_email", ask_admin_email())
connector.load_credentials(raw) Prevention
- Use the app's credential UI, which injects the admin key automatically
- When scripting, template the credential JSON with the admin key pre-filled
- Validate shape with is_complete_gdrive_creds() before persisting
When it happens
Trigger: Passing a credentials dict that was built for another connector type, a hand-written token JSON without the primary_admin_email field, or an export/migration that dropped the key.
Common situations: Users pasting the OAuth token blob (client_id/refresh_token only) directly into the credential field without the surrounding structure the app expects, upgrading between versions where the key name changed, CI scripts writing minimal credentials.
Related errors
- message.compileNotSupported
- main() must return a value. Use null for an empty result.
- Failed to fetch memory detail
- Failed to update memory
- Failed to fetch search detail
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/de60d17d6d0802d7.
Report an issue: GitHub.