{"record":{"id":"742b71e188c403a2","repo":"openclaw/openclaw","slug":"missing-chrome-extension-origin","errorCode":null,"errorMessage":"missing Chrome extension origin","messagePattern":"missing Chrome extension origin","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/browser/src/browser/extension-native-host.ts","lineNumber":60,"sourceCode":"  for (let index = 0; index < argv.length; index += 1) {\n    const argument = argv[index];\n    if (argument === \"--expected-origin\") {\n      const value = argv[index + 1];\n      if (!value || callerOrigin) {\n        throw new Error(\"invalid expected-origin arguments\");\n      }\n      expectedOrigins.push(value);\n      index += 1;\n    } else if (argument?.startsWith(\"chrome-extension://\")) {\n      if (callerOrigin) {\n        throw new Error(\"multiple Chrome extension origins\");\n      }\n      callerOrigin = argument;\n    }\n  }\n  validateExpectedOrigins(expectedOrigins);\n  if (!EXTENSION_ORIGIN_PATTERN.test(callerOrigin)) {\n    throw new Error(\"missing Chrome extension origin\");\n  }\n  return { expectedOrigins, callerOrigin };\n}\n\nasync function validateOwnedFile(filePath: string, executable: boolean): Promise<string> {\n  const resolved = path.resolve(filePath);\n  const info = await fs.lstat(resolved);\n  if (!info.isFile() || info.isSymbolicLink()) {\n    throw new Error(\"unsafe file type\");\n  }\n  if (process.platform !== \"win32\") {\n    const uid = process.getuid?.();\n    if (uid !== undefined && info.uid !== uid) {\n      throw new Error(\"foreign file owner\");\n    }\n    const mode = info.mode & 0o777;\n    if ((mode & 0o077) !== 0 || (executable && (mode & 0o100) === 0)) {\n      throw new Error(\"unsafe file mode\");","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/browser/src/browser/extension-native-host.ts#L42-L78","documentation":"parseBrowserNativeHostOrigins requires exactly one chrome-extension:// caller origin that matches EXTENSION_ORIGIN_PATTERN. If no callerOrigin was captured (the loop found no chrome-extension:// argument) the pattern test on the empty string fails and throws.","triggerScenarios":"The native host argv has no argument starting with 'chrome-extension://', so callerOrigin stays empty and fails EXTENSION_ORIGIN_PATTERN.","commonSituations":"Chrome did not pass the caller origin (older runtime); a launcher script is missing the caller origin argument; testing the native host manually without appending a caller origin.","solutions":["Ensure the launcher passes a chrome-extension://<32-char-id>/ caller origin argument.","Regenerate the launcher via install.","When testing manually, append a valid chrome-extension:// origin as the final argument."],"exampleFix":"// before: no caller origin\nparseBrowserNativeHostOrigins([\"--expected-origin\", \"chrome-extension://abcdefghijklmnopqrstuvwxyz123456/\"]);\n// after: append the caller origin\nparseBrowserNativeHostOrigins([\n  \"--expected-origin\", \"chrome-extension://abcdefghijklmnopqrstuvwxyz123456/\",\n  \"chrome-extension://abcdefghijklmnopqrstuvwxyz123456/\",\n]);","handlingStrategy":"validation","validationCode":"const EXTENSION_ORIGIN_PATTERN = /^chrome-extension:\\/\\/[a-p]{32}\\/$/;\nfunction hasCallerOrigin(argv: string[]): boolean {\n  return argv.some((a) => typeof a === \"string\" && EXTENSION_ORIGIN_PATTERN.test(a));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the launcher appends a valid chrome-extension://<id>/ caller origin.","When testing manually, append a valid caller origin as the final argv element."],"tags":["browser-extension","native-messaging","validation","arguments"],"backgroundTag":null,"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}