{"record":{"id":"dc7625e3eb3efa3e","repo":"RocketChat/Rocket.Chat","slug":"invalid-url","errorCode":"invalid-url","errorMessage":"Invalid url","messagePattern":"Invalid url","errorType":"exception","errorClass":"InvalidUrlError","httpStatus":null,"severity":"warning","filePath":"apps/meteor/client/hooks/useExternalLink.ts","lineNumber":8,"sourceCode":"import { useCallback } from 'react';\n\nimport { InvalidUrlError } from '../lib/errors/InvalidUrlError';\n\nexport const useExternalLink = () => {\n\treturn useCallback((url: string | undefined) => {\n\t\tif (!url) {\n\t\t\tthrow new InvalidUrlError();\n\t\t}\n\t\twindow.open(url, '_blank', 'noopener noreferrer');\n\t}, []);\n};\n","sourceCodeStart":1,"sourceCodeEnd":13,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/client/hooks/useExternalLink.ts#L1-L13","documentation":"Thrown by the useExternalLink hook as an InvalidUrlError (a RocketChatError with error id 'invalid-url') when the supplied url is falsy — undefined, null, or empty string. The guard prevents window.open from being called with a blank target, which would open about:blank. Because it is a typed RocketChatError, callers can distinguish it via the `error` field equal to 'invalid-url'.","triggerScenarios":"Calling the returned callback with no argument, with undefined (e.g. a link property not yet loaded from the API), or with '' (a trimmed/empty string from a malformed record). The check is purely `if (!url)` so any falsy value triggers it before window.open.","commonSituations":"A message/custom-field link whose value is still loading; a deleted/unset external URL field rendered before the fetch resolves; an integration webhook payload with a missing URL; user-supplied content where the URL was stripped by the sanitizer.","solutions":["Guard the call site: only invoke the callback when the URL is a non-empty string.","If the URL may be undefined while loading, render a disabled link or a placeholder until it resolves.","Catch InvalidUrlError specifically (check e.error === 'invalid-url') and show a friendly toast instead of letting it bubble.","Validate/normalize the URL at the data source (API/integration) so empty strings never reach the renderer."],"exampleFix":"// before\nconst openExternal = useExternalLink();\nopenExternal(message.externalUrl);\n\n// after\nconst openExternal = useExternalLink();\nif (message.externalUrl) {\n  openExternal(message.externalUrl);\n}","handlingStrategy":"validation","validationCode":"const openExternal = useExternalLink();\nif (typeof url === 'string' && url.trim().length > 0) {\n  openExternal(url);\n}","typeGuard":"function isNonEmptyUrl(url: unknown): url is string {\n  return typeof url === 'string' && url.trim().length > 0;\n}","tryCatchPattern":"try {\n  openExternal(url);\n} catch (e) {\n  if (e instanceof InvalidUrlError && e.error === 'invalid-url') {\n    dispatchToast({ type: 'warning', message: t('No_url_available') });\n  }\n}","preventionTips":["Render external-link buttons only when the URL is loaded and non-empty.","Catch InvalidUrlError by checking e.error === 'invalid-url' to distinguish it.","Sanitize/normalize URL fields at the data source."],"tags":["external-link","validation","typed-error","url"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}