{"record":{"id":"9570f7728b33963f","repo":"RocketChat/Rocket.Chat","slug":"invalid-parameter","errorCode":null,"errorMessage":"invalid-parameter","messagePattern":"invalid-parameter","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/client/lib/getPermaLink.ts","lineNumber":17,"sourceCode":"import type { IMessage, Serialized } from '@rocket.chat/core-typings';\n\nimport { getUserId } from './user';\n\nconst getMessage = async (msgId: string): Promise<Serialized<IMessage> | null> => {\n\ttry {\n\t\tconst { sdk } = await import('./SDKClient');\n\t\tconst { message } = await sdk.rest.get('/v1/chat.getMessage', { msgId });\n\t\treturn message;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nexport const getPermaLink = async (msgId: string): Promise<string> => {\n\tif (!msgId) {\n\t\tthrow new Error('invalid-parameter');\n\t}\n\n\tconst { Messages, Rooms, Subscriptions } = await import('../stores');\n\n\tconst msg = Messages.state.get(msgId) || (await getMessage(msgId));\n\tif (!msg) {\n\t\tthrow new Error('message-not-found');\n\t}\n\tconst roomData = Rooms.state.get(msg.rid);\n\n\tif (!roomData) {\n\t\tthrow new Error('room-not-found');\n\t}\n\n\tconst subData = Subscriptions.state.find((record) => record.rid === roomData._id && record.u._id === getUserId());\n\n\tconst { roomCoordinator } = await import('./rooms/roomCoordinator');\n","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/2f18297792c973da326be7253f497d239ea6f2b0/apps/meteor/client/lib/getPermaLink.ts#L1-L35","documentation":"getPermaLink() throws 'invalid-parameter' as its very first guard when msgId is falsy (undefined, null, empty string) — before any store lookup or REST call. It exists because the function builds a URL with ?msg= and cannot do so meaningfully without an id.","triggerScenarios":"Calling getPermaLink(undefined), getPermaLink(''), or getPermaLink(null) — typically a caller passing a message object instead of its _id, or a route/search param that is absent.","commonSituations":"Copy-link handlers invoked before a message is selected; msgId destructured from an optional route param; refactors where the IMessage object was passed instead of message._id.","solutions":["Pass the message id explicitly: getPermaLink(message._id).","Guard the call site: skip when the id is missing.","Type the parameter as string and lint against passing objects."],"exampleFix":"// before\nconst link = await getPermaLink(message); // object → msgId undefined → throws\n\n// after\nconst link = message._id ? await getPermaLink(message._id) : null;","handlingStrategy":"validation","validationCode":"const id = typeof msgOrId === 'string' ? msgOrId : msgOrId?._id;\nif (!id) return; // never call getPermaLink without an id\nconst url = await getPermaLink(id);","typeGuard":"const hasMessageId = (v: IMessage | string | undefined): v is string =>\n  typeof v === 'string' ? v.length > 0 : Boolean(v?._id);","tryCatchPattern":"try {\n  const url = await getPermaLink(msgId);\n} catch (e) {\n  if (e instanceof Error && e.message === 'invalid-parameter') return; // caller bug: fix the id source\n  throw e;\n}","preventionTips":["Always pass message._id, never the message object.","Type the parameter as string so object misuse fails at compile time.","Guard optional route/search params before invoking."],"tags":["permalink","validation","parameters"],"backgroundTag":"invalid-parameter","analyzedSha":"2f18297792c973da326be7253f497d239ea6f2b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}