infiniflow/ragflow · critical · Error

main() must be defined or exported.

Error message

main() must be defined or exported.

What it means

Same guard as error 624, but on the permission-sync path retrieve_all_slim_docs_perm_sync: the connector was asked to enumerate document IDs for ACL sync before any GitLab client exists because load_credentials() never ran.

Source

Thrown at internal/agent/sandbox/result_protocol.go:109

// syntax-significant characters) and decoded at runtime via
// JSON.parse(Buffer.from(..., 'base64').toString('utf8')), so the
// only Go-side dataflow into the JS source is the base64 string.
func BuildJavaScriptWrapper(code, argsJSON string) string {
	argsB64 := base64.StdEncoding.EncodeToString([]byte(argsJSON))
	// Note: this string is *embedded inside* a Go raw string, but the
	// Go raw string and the JS source are independent languages. We
	// need the final JS to be valid; the doubled braces {{ }} are JS
	// 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.
//

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Confirm the connector credential exists and load_credentials() succeeded before scheduling perm sync
  2. Re-create or re-enter the GitLab credential if it was deleted
  3. Gate perm-sync scheduling on a successful validate_connector_settings() run

Example fix

# before
slim = connector.retrieve_all_slim_docs_perm_sync()  # no creds loaded

# after
connector.load_credentials(creds)
connector.validate_connector_settings()
slim = connector.retrieve_all_slim_docs_perm_sync()
Defensive patterns

Strategy: validation

Validate before calling

def perm_sync_ready(connector) -> bool:
    return connector.gitlab_client is not None and connector.credentials_loaded

Try / catch

from common.data_source.exceptions import ConnectorMissingCredentialError

try:
    slim = connector.retrieve_all_slim_docs_perm_sync(cb)
except ConnectorMissingCredentialError:
    reattach_credentials(connector)  # reload from credential store, reschedule
    slim = connector.retrieve_all_slim_docs_perm_sync(cb)

Prevention

When it happens

Trigger: Invoking retrieve_all_slim_docs_perm_sync(callback) on a freshly constructed connector, or a perm-sync job scheduled against a connector whose credential payload failed to parse so the client was never set.

Common situations: Background perm-sync job racing ahead of credential provisioning, deleted/expired credential entry removed while the perm-sync task still references the connector.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/bd18f1443fe1e0a9. Report an issue: GitHub.