{"record":{"id":"bd18f1443fe1e0a9","repo":"infiniflow/ragflow","slug":"main-must-be-defined-or-exported-bd18f1","errorCode":null,"errorMessage":"main() must be defined or exported.","messagePattern":"main\\(\\) must be defined or exported\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"internal/agent/sandbox/result_protocol.go","lineNumber":109,"sourceCode":"// syntax-significant characters) and decoded at runtime via\n// JSON.parse(Buffer.from(..., 'base64').toString('utf8')), so the\n// only Go-side dataflow into the JS source is the base64 string.\nfunc BuildJavaScriptWrapper(code, argsJSON string) string {\n\targsB64 := base64.StdEncoding.EncodeToString([]byte(argsJSON))\n\t// Note: this string is *embedded inside* a Go raw string, but the\n\t// Go raw string and the JS source are independent languages. We\n\t// need the final JS to be valid; the doubled braces {{ }} are JS\n\t// template-literal escapes only on the JS side. We pass them\n\t// through as-is.\n\treturn code + `\n\nconst __ragflowArgsB64 = \"` + argsB64 + `\";\nconst __ragflowArgs = JSON.parse(Buffer.from(__ragflowArgsB64, 'base64').toString('utf8'));\n\n(async () => {\n  const __ragflowMain = typeof main !== 'undefined' ? main : module.exports && module.exports.main;\n  if (typeof __ragflowMain !== 'function') {\n    throw new Error('main() must be defined or exported.');\n  }\n  const output = await Promise.resolve(__ragflowMain(__ragflowArgs));\n  if (typeof output === 'undefined') {\n    throw new Error('main() must return a value. Use null for an empty result.');\n  }\n  const payload = JSON.stringify({ present: true, value: output, type: 'json' });\n  if (typeof payload === 'undefined') {\n    throw new Error('main() returned a non-JSON-serializable value.');\n  }\n  console.log('` + resultMarkerPrefix + `' + Buffer.from(payload, 'utf8').toString('base64'));\n})();\n`\n}\n\n// ExtractStructuredResult scans stdout for the marker line, decodes\n// the JSON payload after it, and returns the user-visible stdout\n// (with the marker line removed) plus the parsed structured result.\n//","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/internal/agent/sandbox/result_protocol.go#L91-L127","documentation":"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.","triggerScenarios":"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.","commonSituations":"Background perm-sync job racing ahead of credential provisioning, deleted/expired credential entry removed while the perm-sync task still references the connector.","solutions":["Confirm the connector credential exists and load_credentials() succeeded before scheduling perm sync","Re-create or re-enter the GitLab credential if it was deleted","Gate perm-sync scheduling on a successful validate_connector_settings() run"],"exampleFix":"# before\nslim = connector.retrieve_all_slim_docs_perm_sync()  # no creds loaded\n\n# after\nconnector.load_credentials(creds)\nconnector.validate_connector_settings()\nslim = connector.retrieve_all_slim_docs_perm_sync()","handlingStrategy":"validation","validationCode":"def perm_sync_ready(connector) -> bool:\n    return connector.gitlab_client is not None and connector.credentials_loaded","typeGuard":null,"tryCatchPattern":"from common.data_source.exceptions import ConnectorMissingCredentialError\n\ntry:\n    slim = connector.retrieve_all_slim_docs_perm_sync(cb)\nexcept ConnectorMissingCredentialError:\n    reattach_credentials(connector)  # reload from credential store, reschedule\n    slim = connector.retrieve_all_slim_docs_perm_sync(cb)","preventionTips":["Schedule perm-sync only after a successful validate_connector_settings()","Persist credential IDs with connectors so background jobs can reload them","Alert when a credential referenced by a scheduled job is deleted"],"tags":["gitlab","lifecycle","permissions-sync","credentials","connector"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}