{"record":{"id":"007601187fe2624c","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-subscription","errorCode":"error-invalid-subscription","errorMessage":"Invalid subscription","messagePattern":"Invalid subscription","errorType":"exception","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/rooms.ts","lineNumber":467,"sourceCode":"\t{\n\t\tauthRequired: true,\n\t\tbody: saveDraftBodySchema,\n\t\tresponse: {\n\t\t\t200: saveDraftResponseSchema,\n\t\t\t400: validateBadRequestErrorResponse,\n\t\t\t401: validateUnauthorizedErrorResponse,\n\t\t},\n\t},\n\tasync function action() {\n\t\tconst { rid, draft, tmid } = this.bodyParams;\n\n\t\tif (draft.length > (settings.get<number>('Message_MaxAllowedSize') ?? 0)) {\n\t\t\treturn API.v1.failure('error-message-size-exceeded');\n\t\t}\n\n\t\tconst subscription = await Subscriptions.updateDraftByRoomIdAndUserId(rid, this.userId, draft || undefined, tmid);\n\t\tif (!subscription) {\n\t\t\tthrow new Meteor.Error('error-invalid-subscription', 'Invalid subscription');\n\t\t}\n\n\t\tvoid notifyOnSubscriptionChanged(subscription);\n\n\t\treturn API.v1.success();\n\t},\n);\n\nAPI.v1.post(\n\t'rooms.cleanHistory',\n\t{\n\t\tauthRequired: true,\n\t\tbody: isRoomsCleanHistoryProps,\n\t\tresponse: {\n\t\t\t200: ajv.compile<{ _id: string; count: number }>({\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\t_id: { type: 'string' },","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/e4b8178b205510181a96ceefee043d0abcd13e5a/apps/meteor/server/api/v1/rooms.ts#L449-L485","documentation":"Thrown by POST /api/v1/rooms.saveDraft when Subscriptions.updateDraftByRoomIdAndUserId(rid, userId, draft, tmid) returns null, which happens when the caller has no subscription document for that room, i.e. they never joined it. Drafts are stored on the user's subscription, so non-members cannot save one. A separate earlier check returns error-message-size-exceeded when draft exceeds Message_MaxAllowedSize.","triggerScenarios":"POST rooms.saveDraft with a rid the authenticated user is not subscribed to: never-joined public channel, private channel they were removed from, another user's DM room, or a rid typo.","commonSituations":"Bots writing drafts into channels they only have permission to read but never joined; users removed from a private room whose stale client still syncs drafts; state restoration tools replaying drafts onto a fresh account; room ids confused between threads and channels.","solutions":["Join the room first (POST /api/v1/rooms.join or the subscription flow), then re-send the draft","Verify membership with GET /api/v1/subscriptions.get?rid=... before saving","Drop drafts for rooms the user left instead of erroring","Confirm the rid is the room the draft belongs to, not a thread message id"],"exampleFix":"// before\nawait sdk.post('rooms.saveDraft', { rid: privateChannelId, draft: 'hello' }); // not a member\n\n// after\nconst { subscription } = await sdk.get('subscriptions.get', { rid: privateChannelId }).catch(() => ({ subscription: null }));\nif (!subscription) await sdk.post('rooms.join', { rid: privateChannelId });\nawait sdk.post('rooms.saveDraft', { rid: privateChannelId, draft: 'hello' });","handlingStrategy":"validation","validationCode":"const { subscription } = await sdk.get('subscriptions.get', { rid }).catch(() => ({ subscription: null }));\nif (!subscription) throw new Error(`join room ${rid} before saving a draft`);","typeGuard":null,"tryCatchPattern":"try {\n  await sdk.post('rooms.saveDraft', { rid, draft, tmid });\n} catch (e: any) {\n  if (e?.response?.data?.errorType === 'error-invalid-subscription') {\n    await sdk.post('rooms.join', { rid }); // then retry once\n    return sdk.post('rooms.saveDraft', { rid, draft, tmid });\n  }\n  throw e;\n}","preventionTips":["Sync drafts only for rooms present in the user's subscriptions","Discard drafts on rooms the user left or was removed from","Verify rid is a room id, not a thread/message id"],"tags":["rooms","drafts","subscription","rest-api"],"backgroundTag":"subscription-not-found","analyzedSha":"e4b8178b205510181a96ceefee043d0abcd13e5a","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}