{"record":{"id":"2c9f50a792c340ac","repo":"FlowiseAI/Flowise","slug":"team-id-is-required-to-list-channels","errorCode":null,"errorMessage":"Team ID is required to list channels","messagePattern":"Team ID is required to list channels","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/tools/MicrosoftTeams/core.ts","lineNumber":102,"sourceCode":"            description: 'List all channels in a team',\n            schema: z.object({\n                teamId: z.string().describe('ID of the team to list channels from'),\n                maxResults: z.number().optional().default(50).describe('Maximum number of channels to return')\n            }),\n            baseUrl: BASE_URL,\n            method: 'GET',\n            headers: {}\n        }\n\n        super({ ...toolInput, accessToken: args.accessToken, defaultParams: args.defaultParams })\n    }\n\n    protected async _call(arg: any): Promise<string> {\n        const params = { ...arg, ...this.defaultParams }\n        const { teamId, maxResults = 50 } = params\n\n        if (!teamId) {\n            throw new Error('Team ID is required to list channels')\n        }\n\n        try {\n            const endpoint = `/teams/${teamId}/channels`\n            const result = await this.makeTeamsRequest(endpoint)\n\n            // Filter results to maxResults on client side since $top is not supported\n            const channels = result.value || []\n            const limitedChannels = channels.slice(0, maxResults)\n\n            const responseData = {\n                success: true,\n                channels: limitedChannels,\n                count: limitedChannels.length,\n                total: channels.length\n            }\n\n            return this.formatResponse(responseData, params)","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/tools/MicrosoftTeams/core.ts#L84-L120","documentation":"Thrown synchronously by ListChannelsTool._call when teamId is falsy after merging the tool args with defaultParams. Because the throw sits above the try block at core.ts:101, it propagates out of _call as a real rejection rather than being converted into a formatResponse error string. The schema at core.ts:86 declares teamId as z.string() (non-optional), so a well-behaved zod-validated caller cannot trigger this — it fires only when defaultParams overrides teamId to undefined, when the schema is bypassed, or when an LLM agent sends malformed tool input.","triggerScenarios":"Caller invokes list_channels without teamId; defaultParams contains { teamId: undefined } which overwrites the arg via the { ...arg, ...this.defaultParams } spread at core.ts:98; LangChain agent bypasses zod parsing; the tool is invoked directly in tests with an empty object.","commonSituations":"defaultParams set globally with teamId: '' or undefined intended as 'use agent value' but actually clobbering it; agent prompt not telling the model teamId is required; CI test that constructs the tool and calls _call({}) directly.","solutions":["Confirm the tool input includes a non-empty teamId string before invoking.","Fix defaultParams semantics: the spread { ...arg, ...this.defaultParams } means defaultParams WINS — invert to { ...this.defaultParams, ...arg } so caller args take precedence, or stop putting teamId in defaultParams.","If you genuinely want a default team, put it in defaultParams but ensure the caller omits teamId rather than passing undefined.","Wrap the call site in try/catch since this throw escapes the tool."],"exampleFix":"// before — defaultParams silently overrides caller\nconst params = { ...arg, ...this.defaultParams } // defaultParams wins\n\n// after — caller args win, defaults only fill gaps\nconst params = { ...this.defaultParams, ...arg }","handlingStrategy":"validation","validationCode":"function validateListChannelsInput(input: unknown): asserts input is { teamId: string } {\n  if (typeof input !== 'object' || input === null) throw new Error('input required')\n  const { teamId } = input as any\n  if (typeof teamId !== 'string' || !teamId.trim()) {\n    throw new Error('teamId is required and must be a non-empty string')\n  }\n}","typeGuard":"function isListChannelsArgs(x: unknown): x is { teamId: string; maxResults?: number } {\n  return typeof x === 'object' && x !== null &&\n    typeof (x as any).teamId === 'string' && (x as any).teamId.trim() !== ''\n}","tryCatchPattern":"try {\n  if (!isListChannelsArgs(input)) {\n    return { error: 'teamId required' }\n  }\n  return await listChannelsTool.invoke(input)\n} catch (e) {\n  // validation throws above escape the tool — handle at orchestration\n  log.warn('list_channels validation failed', { error: e })\n  return { error: (e as Error).message }\n}","preventionTips":["Validate IDs at the orchestration layer before invoking the tool — do not rely on zod alone because defaultParams can clobber values post-validation.","Audit defaultParams: any key present there overrides caller args due to the { ...arg, ...this.defaultParams } spread.","Resolve teamId from a prior list_teams call rather than accepting free-text from end users.","Add unit tests that call _call with the minimal expected input to catch regressions."],"tags":["validation","microsoft-teams","zod","required-field","default-params"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}