Skip to content

[Bug] Copilot Studio connector generation mishandles x-ms-conversation-id header and undefined causes conversation stickiness #287

@GBency

Description

@GBency

Describe the bug

The generated Copilot Studio connector service (MicrosoftCopilotStudioService.ts) mishandles the conversation identifier header in two ways:

  1. Wrong key (x_ms_conversation_id) means the conversation id is never sent

    • The generator emits a parameter/property named x_ms_conversation_id.
    • The connector expects a hyphenated header key: x-ms-conversation-id.
    • As a result, even when the caller provides a conversation id, it is never actually transmitted, because the runtime/connector maps only the correct header name.
  2. If the key is corrected but sent as x-ms-conversation-id = undefined, Copilot Studio continues the last conversation

    • When the header name is corrected to x-ms-conversation-id but the value is passed as undefined (first call, when no conversation exists yet), Copilot Studio still responds as if it is continuing the last conversation and returns an id corresponding to the previously registered conversation.

Together these issues prevent correct conversation/session isolation and make it difficult to reliably start a new conversation.

To Reproduce

Case A — Generated code uses x_ms_conversation_id (underscore)

  1. Add/refresh the Copilot Studio connector in a Power Apps Code App project so that service code is generated.
  2. Open the generated file MicrosoftCopilotStudioService.ts.
  3. Call the generated method and provide a conversation id value.
  4. Observe that the conversation id is not actually sent (because the key is wrong), so you cannot reliably control conversation continuation/new conversation behavior.

Case B — Correct header name but value is undefined

  1. Modify the generated code so that the key is "x-ms-conversation-id".
  2. Make the first call passing "x-ms-conversation-id" = undefined (because no conversation is expected to exist yet).
  3. Observe Copilot Studio replies but behaves as if it continued the last conversation, and returns an id matching the previously registered conversation.

Expected behavior

  • The generator should map the conversation id using the correct hyphenated header key: "x-ms-conversation-id".
  • On the first call, if no conversation id is available yet, the header should be omitted entirely (not sent with an undefined value), so a new conversation is started.

Actual behavior

  • Generated code uses x_ms_conversation_id instead of "x-ms-conversation-id", so the conversation id is never transmitted.
  • If "x-ms-conversation-id" is sent with value undefined, Copilot Studio responds continuing the last conversation (sticky session), returning the previous conversation id.

Screenshots / Logs

N/A (can provide if needed). The issue is observable via generated TypeScript and runtime conversation behavior.

Generated code (problematic)

public static async ExecuteCopilotAsyncV2(
  Copilot: string,
  body: Record<string, unknown>,
  x_ms_conversation_id?: string,
  environmentId?: string
): Promise<IOperationResult<Record<string, unknown>>> {
  const params: {
    Copilot: string,
    body: Record<string, unknown>,
    x_ms_conversation_id?: string,
    environmentId?: string
  } = { Copilot, body, x_ms_conversation_id, environmentId };

  const result = await MicrosoftCopilotStudioService.client.executeAsync<
    { Copilot: string, body: Record<string, unknown>, x_ms_conversation_id?: string, environmentId?: string },
    Record<string, unknown>
  >({
    connectorOperation: {
      tableName: MicrosoftCopilotStudioService.dataSourceName,
      operationName: 'ExecuteCopilotAsyncV2',
      parameters: params
    },
  });

  return result;
}

Temporary workaround (works)

My current workaround is:

  • Use the correct key "x-ms-conversation-id".
  • Do not send "x-ms-conversation-id" when it is undefined (first call).
  • Read the conversation id from the first response.
  • On the second prompt (and subsequent calls), send "x-ms-conversation-id" with the conversation id obtained from the first response.

This reliably establishes new conversations.

public static async ExecuteCopilotAsyncV2(
  Copilot: string,
  body: Record<string, unknown>,
  x_ms_conversation_id?: string,
  environmentId?: string
): Promise<IOperationResult<void>> {

  const params: {
    Copilot: string,
    body: Record<string, unknown>,
    "x-ms-conversation-id"?: string,
    environmentId?: string
  } = { Copilot, body, environmentId };

  // IMPORTANT: omit the header entirely when undefined to force a new conversation
  if (x_ms_conversation_id !== undefined) {
    params["x-ms-conversation-id"] = x_ms_conversation_id;
  }

  const result = await MicrosoftCopilotStudioService.client.executeAsync<
    { Copilot: string, body: Record<string, unknown>, "x-ms-conversation-id"?: string, environmentId?: string },
    void
  >({
    connectorOperation: {
      tableName: MicrosoftCopilotStudioService.dataSourceName,
      operationName: 'ExecuteCopilotAsyncV2',
      parameters: params
    },
  });

  return result;
}

Metadata

Metadata

Assignees

Labels

Fix rolling outWe start the rollout with the fix for this issue.bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions