Bug Description
When handling an adaptiveCard/action invoke, context.Activity.Value always returns null, even though the incoming JSON payload contains a valid value field and the debugger shows a non-null Value property of the derived type.
This affects any handler using the base InvokeActivity type:
[Invoke("adaptiveCard/action")]
public async Task<Response> OnInvoke(IContext<InvokeActivity> context)
{
// This always evaluates to null
if (context.Activity.Value != null)
{
// Never gets here
}
}
Root Cause
The derived activity classes (e.g. AdaptiveCards.ActionActivity) use the new keyword to shadow the base Value property with a strongly-typed version:
- Base (
InvokeActivity): public object? Value { get; set; }
- Derived (
AdaptiveCards.ActionActivity): public new required Api.AdaptiveCards.InvokeValue Value { get; set; }
In C#, new creates a separate backing field — it does not override the base property. During JSON deserialization, the converter chain correctly creates the derived type (ActionActivity) and populates the derived Value property. However, the base InvokeActivity.Value is never set, so accessing Value through a base class reference returns null.
Deserialization flow
InvokeActivity.JsonConverter.Read() sees "name": "adaptiveCard/" prefix → delegates to AdaptiveCardActivity
AdaptiveCardActivity.JsonConverter.Read() sees "adaptiveCard/action" → deserializes as AdaptiveCards.ActionActivity
ActionActivity.Value (derived, typed as InvokeValue) gets populated ✅
InvokeActivity.Value (base, typed as object?) stays null ❌
Debugger shows two Value properties
| Property |
Type |
Value |
Value (InvokeActivity) |
object? |
null |
Value (ActionActivity) |
AdaptiveCards.InvokeValue |
has data ✅ |
Affected Classes
This same new shadowing pattern exists across all invoke subtypes:
AdaptiveCards.ActionActivity — new InvokeValue Value
Tasks.FetchActivity / Tasks.SubmitActivity — new TaskModules.Request Value
MessageExtensions.QueryActivity — new MessageExtensions.Query Value
SignIn.TokenExchangeActivity — new SignIn.ExchangeToken Value
HandoffActivity — new HandoffActivityValue Value
SearchActivity — new SearchValue Value
Any handler using a base type reference (InvokeActivity, AdaptiveCardActivity, etc.) will hit this same null issue.
Workaround
Cast to the derived type to access the value:
[Invoke("adaptiveCard/action")]
public async Task<Response> OnInvoke(IContext<InvokeActivity> context)
{
if (context.Activity is AdaptiveCards.ActionActivity actionActivity)
{
var invokeValue = actionActivity.Value; // works
var data = invokeValue.Action.Data;
}
}
Microsoft.Teams.Api.Activities.Invokes.AdaptiveCards.ActionActivity
Possible Fixes
- Option A: During deserialization, also populate the base
InvokeActivity.Value property (sync both backing fields).
- Option B: Have derived class setters sync the base property (e.g. when
ActionActivity.Value is set, also set base.Value = value).
- Option C: Redesign using generics (
IInvokeActivity<TValue>) so the property is properly typed without shadowing.
2.1
|
public class InvokeActivity<TValue> : InvokeActivity |
|
{ |
|
/// <summary> |
|
/// Gets or sets the strongly-typed value associated with the invoke activity. |
|
/// This property shadows the base class Value property but uses the same underlying storage, |
|
/// ensuring no synchronization issues between typed and untyped access. |
|
/// </summary> |
|
public new TValue? Value |
|
{ |
|
get => base.Value != null ? JsonSerializer.Deserialize<TValue>(base.Value.ToJsonString()) : default; |
|
set => base.Value = value != null ? JsonSerializer.SerializeToNode(value) : null; |
|
} |
This uses a generic InvokeActivity<TValue> and extends InvokeActivity with synced base.Value.
Since we have the solution in 2.1, we can leave this bug open for the workaround to be available for customers. We can also discuss the possibility of getting the fix into pre-2.1 as well.
Bug Description
When handling an
adaptiveCard/actioninvoke,context.Activity.Valuealways returnsnull, even though the incoming JSON payload contains a validvaluefield and the debugger shows a non-nullValueproperty of the derived type.This affects any handler using the base
InvokeActivitytype:Root Cause
The derived activity classes (e.g.
AdaptiveCards.ActionActivity) use thenewkeyword to shadow the baseValueproperty with a strongly-typed version:InvokeActivity):public object? Value { get; set; }AdaptiveCards.ActionActivity):public new required Api.AdaptiveCards.InvokeValue Value { get; set; }In C#,
newcreates a separate backing field — it does not override the base property. During JSON deserialization, the converter chain correctly creates the derived type (ActionActivity) and populates the derivedValueproperty. However, the baseInvokeActivity.Valueis never set, so accessingValuethrough a base class reference returnsnull.Deserialization flow
InvokeActivity.JsonConverter.Read()sees"name": "adaptiveCard/"prefix → delegates toAdaptiveCardActivityAdaptiveCardActivity.JsonConverter.Read()sees"adaptiveCard/action"→ deserializes asAdaptiveCards.ActionActivityActionActivity.Value(derived, typed asInvokeValue) gets populated ✅InvokeActivity.Value(base, typed asobject?) staysnull❌Debugger shows two Value properties
Value(InvokeActivity)object?nullValue(ActionActivity)AdaptiveCards.InvokeValueAffected Classes
This same
newshadowing pattern exists across all invoke subtypes:AdaptiveCards.ActionActivity—new InvokeValue ValueTasks.FetchActivity/Tasks.SubmitActivity—new TaskModules.Request ValueMessageExtensions.QueryActivity—new MessageExtensions.Query ValueSignIn.TokenExchangeActivity—new SignIn.ExchangeToken ValueHandoffActivity—new HandoffActivityValue ValueSearchActivity—new SearchValue ValueAny handler using a base type reference (
InvokeActivity,AdaptiveCardActivity, etc.) will hit this same null issue.Workaround
Cast to the derived type to access the value:
Microsoft.Teams.Api.Activities.Invokes.AdaptiveCards.ActionActivityPossible Fixes
InvokeActivity.Valueproperty (sync both backing fields).ActionActivity.Valueis set, also setbase.Value = value).IInvokeActivity<TValue>) so the property is properly typed without shadowing.2.1
teams.net/core/src/Microsoft.Teams.Bot.Apps/Schema/Activities/InvokeActivity.cs
Lines 73 to 84 in c888afb
This uses a generic
InvokeActivity<TValue>and extendsInvokeActivitywith syncedbase.Value.Since we have the solution in 2.1, we can leave this bug open for the workaround to be available for customers. We can also discuss the possibility of getting the fix into pre-2.1 as well.