azuremicrosoft-graph-apimicrosoft-graph-sdks

Graph API Open Extension on Calendar Events causing an error


Im creating and reading Calendar Events through c# and the Graph API for delegated users. This works just fine. Atleast with Events when i dont try and use Open Extensions.

var precomExtension = new OpenTypeExtension
{
    OdataType = "microsoft.graph.openTypeExtension",
    ExtensionName = "com.zxcasd.zxcasdExtensions",
    AdditionalData = new Dictionary<string, object>
    {
        {
            "zxcasd" , "1234586"
        }
    }
};

var @event = new Event
{
    Subject = "Kalle Anka",
    Body = new ItemBody
    {
        ContentType = BodyType.Text,
        Content = "This is a test from zxczxc, sorry for any inconvinience it may cause"
    },
    Start = new DateTimeTimeZone
    {
        DateTime = "2025-12-24T14:00:00",
        TimeZone = "Pacific Standard Time"
    },
    End = new DateTimeTimeZone
    {
        DateTime = "2025-12-24T15:00:00",
        TimeZone = "Pacific Standard Time"
    },
    Location = new Location
    {
        DisplayName = "Conference Room"
    },
    Extensions = new List<Extension>
    {
        precomExtension
    },
};

try
{
    await graphClient.Users[userEmail].Events.PostAsync(@event);
    Console.WriteLine("Event created successfully!");
}
catch (ServiceException ex)
{
    Console.WriteLine($"Error creating event: {ex.Message}");
}

I then try and read the calendar events with this code

var events3 = await graphServiceClient
    .Users[user]
    .Events
    .GetAsync(requestConfiguration =>
    {
        requestConfiguration.QueryParameters.Select = new string[] { "Id", "Subject", "Start", "End", "Organizer", "createdDateTime", "lastModifiedDateTime" };
        //requestConfiguration.QueryParameters.Expand = new string[] { "extensions" };
        requestConfiguration.QueryParameters.Filter = $"start/dateTime ge '{startDateString}' and end/dateTime le '{endDateString}'";
        requestConfiguration.QueryParameters.Orderby = new string[] { $"start/dateTime" };
        requestConfiguration.QueryParameters.Top = 9999;
    });

if (events3?.Value?.Count() > 0)
{
    Console.WriteLine($"Nr of retrived events: {events3.Value.Count}");
    Console.WriteLine("*********************************");
}
foreach (var eventItem in events3.Value)
{
    Console.WriteLine($"Id: {eventItem.Id}");
    Console.WriteLine($"Start: {eventItem.Start.DateTime}");
    Console.WriteLine($"End: {eventItem.End.DateTime}");
    Console.WriteLine($"Subject: {eventItem.Subject}");
    Console.WriteLine($"Organizer: {eventItem.Organizer?.EmailAddress?.Name ?? "No organizer"}");
    Console.WriteLine($"Created: {eventItem.CreatedDateTime}");
    Console.WriteLine($"LastModified: {eventItem.LastModifiedDateTime}");
    //Console.WriteLine(eventItem.Body.Content);
    //Access other event properties as needed
    Console.WriteLine("*********************************");
}

Thats works just fine, until i uncomment the extensions line

//requestConfiguration.QueryParameters.Expand = new string[] { "extensions" };

Now i cant retrive any events at all. The exception i get is this:

{"Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. LineNumber: 0 | BytePositionInLine: 437."}
    BytePositionInLine: 437
    Data: {System.Collections.ListDictionaryInternal}
    HResult: -2146233088
    HelpLink: null
    InnerException: null
    LineNumber: 0
    Message: "Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. LineNumber: 0 | BytePositionInLine: 437."
    Path: null
    Source: "System.Text.Json"
    StackTrace: "   at System.Text.Json.Utf8JsonReader.ReadSingleSegment()\r\n   at System.Text.Json.Utf8JsonReader.Read()\r\n   at System.Text.Json.JsonDocument.Parse(ReadOnlySpan`1 utf8JsonSpan, JsonReaderOptions readerOptions, MetadataDb& database, StackRowStack& stack)\r\n   at System.Text.Json.JsonDocument.Parse(ReadOnlyMemory`1 utf8Json, JsonReaderOptions readerOptions, Byte[] extraRentedArrayPoolBytes, PooledByteBufferWriter extraPooledByteBufferWriter)\r\n   at System.Text.Json.JsonDocument.<ParseAsyncCore>d__65.MoveNext()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory.<GetRootParseNodeAsync>d__7.MoveNext()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at M
icrosoft.Kiota.Abstractions.Serialization.ParseNodeProxyFactory.<GetRootParseNodeAsync>d__8.MoveNext()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Microsoft.Kiota.Abstractions.Serialization.ParseNodeFactoryRegistry.<GetRootParseNodeAsync>d__9.MoveNext()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Microsoft.Kiota.Abstractions.Serialization.ParseNodeProxyFactory.<GetRootParseNodeAsync>d__8.MoveNext()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Microsoft.Kiota.Http.HttpClientLibrary.HttpClientRequestAdapter.<GetRootParseNodeAsync>d__30.MoveNext()\r\n   at System.Run
time.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Microsoft.Kiota.Http.HttpClientLibrary.HttpClientRequestAdapter.<SendAsync>d__20`1.MoveNext()\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at Microsoft.Kiota.Http.HttpClientLibrary.HttpClientRequestAdapter.<SendAsync>d__20`1.MoveNext()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Microsoft.Graph.Users.Item.Events.EventsRequestBuilder.<GetAsync>d__8.MoveNext()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n   at AzureCalendar.Program.<GetCalendarAppoi
ntments>d__5.MoveNext() in E:\\_Code_\\Testing\\AzureCalendar\\AzureCalendar\\Program.cs:line 229"
    TargetSite: {Boolean ReadSingleSegment()}

So, what am i doing wrong? I cant find anything in the documentation about this: https://learn.microsoft.com/en-us/graph/extensibility-open-users?tabs=csharp Im assuming that something breaks while trying to expand extension where there are none? Not all Calendar Events have been created with a Extension.


Solution

  • I think that with $expand=extensions the Graph API returns the error

    {
      "value":[
        {
          "error": { 
            "code":"ErrorGraphExtensionExpandRequiresFilter",
            "message":"When expanding extensions, a filter must be provided to specify which extensions to expand. For example $expand=Extensions($filter=Id eq 'Com.Insightly.CRMOpportunity')."
          }
        }
      ]
    }
    

    So, you need to use nested filter in $expand like extensions($filter=id eq 'com.zxcasd.zxcasdExtensions')

    var events3 = await graphServiceClient
    .Users[user]
    .Events
    .GetAsync(requestConfiguration =>
    {
        requestConfiguration.QueryParameters.Select = new string[] { "Id", "Subject", "Start", "End", "Organizer", "createdDateTime", "lastModifiedDateTime" };
        requestConfiguration.QueryParameters.Expand = new string[] { "extensions($filter=id eq 'com.zxcasd.zxcasdExtensions')" };
        requestConfiguration.QueryParameters.Filter = $"start/dateTime ge '{startDateString}' and end/dateTime le '{endDateString}'";
        requestConfiguration.QueryParameters.Orderby = new string[] { $"start/dateTime" };
        requestConfiguration.QueryParameters.Top = 9999;
    });