msgraph-sdk-dotnet icon indicating copy to clipboard operation
msgraph-sdk-dotnet copied to clipboard

Issues moving LOB app from EWS (on-premise) to Microsoft Graph

Open bf-ckiendl opened this issue 4 years ago • 23 comments

Describe the bug The entire thing just seems to be randomly f░cked.

If I try to send a Message with an odata type in the payload, as the SDK generates it, the response says "A type named 'microsoft.graph.message' could not be resolved by the model." Right. Since this kind of :shit: has been going on for a while and nobody bothered to fix it, affected users have already come up with a solution: Null out the odata type in the request object. I'm actually using the SDK because I didn't want to f░ck around with the request manually, but whatever.

Surprise! If you leave out the odata type, you get transported into the past and for some broken reason, despite the fact that you're using the Graph SDK to talk to the Graph endpoint about sending a Graph Message, Graph now assumes you're trying to use the deprecated Microsoft.OutlookServices.Message type. Deprecated. Should be old and time-tested, right?

Hell no.

"The property 'subject' does not exist on type 'Microsoft.OutlookServices.Message'. Make sure to only use property names that are defined by the type or mark the type as open type." "The property 'body' does not exist on type 'Microsoft.OutlookServices.Message'. Make sure to only use property names that are defined by the type or mark the type as open type."

Right. Because there's definitely no subject or body on e-mail messages. And not in the documentation. Or the official, working Graph Explorer example. Mmhmm.

I am signed into the Graph Explorer with the same user. I can take the exact URL and the exact payload the SDK sent, odata types and all, copy-paste them into Graph Explorer and the request works fine. So I know with certainty it's not a limitation of my user, I know with certainty it's not the test data (which is really just the Graph Explorer example with my e-mail-address anyway), and I know with certainty that it's not the backend complaining about the SDK's inclusion of odata types. I also have the SDK working with Message retrieval, including attachments, so I know with certainty that my setup is not generally broken and it's not an authentication error or anything else like that. I also know from experience that wrong permissions end in something like a 403 status code. So even if I hadn't checked the permissions, I would rule out permission issues.

Whatever causes those requests to fail with weird and completely unreasonable errors stems from the SDK.

To Reproduce

  • Try to do exactly what the documentation says.
  • Send exactly the message previously tested as working in Graph Explorer
  • Fail
  • Be utterly confused
  • Get frustrated by the complete lack of documentation, debugging tools and debugging data

Expected behavior I expect the f░cking thing to work. I expect it to give reasonable error messages so I can diagnose problems, instead of making the error message itself a secondary problem.

I also sort of expected there to be an actual documentation, you know...like a reference telling me what classes, properties and methods this SDK contains. Instead, I have to guess how to proceed based on similarities to HTTP URIs and three-year-old Stack Overflow answers. Yeah, sure, there are C# examples in the Graph API documentation, but for one, it took me a while to find them, because the entire point of using the SDK was to have the API abstracted away, so why would I look at the API usage examples and guess that the API usage would show SDK usage, rather than HTTP API usage through HttpClient, and two, the vast majority of them only demonstrate a very narrow use case and only scratch the surface of the available functionality. They're not documentation. They're examples. And the text around them is strongly focused on how to do :shit: manually via HTTP requests.

Guessing the procedure is also not helped by the fact that the SDK seems to make up its own URIs. One of the things I had to test for was whether https://graph.microsoft.com/v1.0/me/microsoft.graph.sendMail instead of https://graph.microsoft.com/v1.0/me/sendMail was just plain wrong, but no. It's just more undocumented :shit: making my life harder. Works fine in Graph Explorer. Just not in the SDK.

I also kind of expected developers to at least care enough about their users to be nice enough to give hints regarding common errors. The whole "The property 'foo' does not exist on type 'Microsoft.Whatever.Fckery'" issue has been mentioned hundreds of times on SO, various Microsoft platforms and elsewhere, and yet, it seems like no developer ever had the mercy to say "if you get that error, it's usually caused by...".

Honestly, this is the worst experience I have ever had trying to start with a new API, SDK, backend or whatever. I have written software around things that were completely undocumented and not even designed to have software written around them, and it was more straightforward than this.

Client version v3.30.0

Desktop (please complete the following information):

  • Windows 10 Pro 20H2 Build 19042.928
  • Microsoft Visual Studio Community 2019 Version 16.9.4

AB#9256

bf-ckiendl avatar May 04 '21 08:05 bf-ckiendl

This :shit: is the most broken bull:shit: I have ever seen. I have no illusion the developers care enough to fix this (otherwise this wouldn't have arisen in the first place), but to the poor souls who stand beside me in the abyss: Regardless of the completely unrelated error message, this seems to be related to whether your mailbox is on premises or not.

Yes, I know. It works fine in Graph Explorer with the exact same mailbox. Without error. In less than a second. It doesn't matter. Because it's all broken bull:shit: that makes no sense.

I try a given test message from my on-premises-mailbox in Graph Explorer, it works. I try the same test message from my on-premises-mailbox from the application through the SDK, I get

Status Code: BadRequest
Microsoft.Graph.ServiceException: Code: RequestBodyRead
Message: A type named 'microsoft.graph.message' could not be resolved by the model. When a model is available, each type name must resolve to a valid type. [...]

I try the same test message from a cloud shared mailbox from the application through the SDK, and it works fine. Just like that.

And when I say "the same message", I mean the same message. From is still set to my e-mail address and the message is still sent as coming from mailbox. No sent on behalf of or anything. It looks just as if I had sent it myself. There is no indicator in the headers or elsewhere the mail was sent through a shared mailbox.

Basically, sending from my on-premises-mailbox doesn't work, but sending as me from a cloud mailbox works just fine.

The only difference is that the sent e-mail is in the shared mailbox's sent items folder and not in mine.

So clearly the Message itself is perfectly fine. It is entirely related to whether I'm trying to use an on-premises-mailbox specifically through the MS Graph SDK.

And I'm supposed to gather that from "A type named 'microsoft.graph.message' could not be resolved by the model.". :roll_eyes:

Dear Microsoft: Get your f░cking error messages straight. And if something doesn't work, don't fudge it in Graph Explorer to pretend it works even if it doesn't.

bf-ckiendl avatar May 04 '21 11:05 bf-ckiendl

To put this in actionable terms, so the devs who f░cked this up don't get an excuse to close this and pretend it never existed:

Of course this wreck doesn't give any sort of helpful feedback, so I have to guess, but I'm assuming based on the clear difference in functionality between on-premises and cloud mailboxes and the previous references to Microsoft.OutlookServices.Message that Azure/Graph quietly redirected the requests to the on-premises-mailbox to the actual on-premises Exchange server, and that's why :shit: went sideways.

If try to open an MP3 file with Microsoft Excel, I get a very clear error message telling me Excel can't open that file. If I try to use an IMAP client to connect to an SFTP server, I get a very clear error message telling me the connection isn't possible.

Neither client pretends to work and then pretends that the user's actions within a supposedly proper interaction are to blame for a lack of success.

So if I'm using the Microsoft Graph SDK to send Microsoft Graph resource types to a Microsoft Graph API endpoint, and the requests ends up at an entirely different server that is decidedly not MS Graph, I expect a very clear error message informing me that the expected request could not be completed.

If the backend were designed properly, it would send a 3xx redirection status code in the first place, making the redirect visible, but if that's not happening, at least the client could have the decency of informing the developer "Oh, by the way: That Graph response you received? It didn't come from Graph.".

To send the data to an entirely different, incompatible system and to serve back an unexplained error message that makes no sense in the context of the request is just plain bad form and certainly violates best practices, e.g. explicit being better than implicit etc.

If you must have that functionality, at least require an override for it. A boolean parameter along the lines of "set this to true if you want Graph to quietly send your data somewhere else entirely".

But to have a developer spend days trying to diagnose Graph errors when Graph isn't the issue and the errors aren't coming from Graph is...not particularly friendly.

bf-ckiendl avatar May 04 '21 13:05 bf-ckiendl

Geezus OP... feel better?

For others... want an example of how to NOT address something?

... now have fun reading this: https://opensource.microsoft.com/codeofconduct/

andrewconnell avatar May 04 '21 18:05 andrewconnell

I do, in fact, feel better. Because after massive amounts of time sunk into this, I finally got confirmation I was, in fact, neither insane nor incompetent. And I can continue with the work I was tasked with, knowing that I did it right from the very start, and was simply lied to by Microsoft's tools.

Your response, unfortunately, is a prime example of how not to interact with users of a product, be they paying or not.

The very fact that the product generated this much anger is, by default, a Bad Thing™. I'm gonna assume "let's totally kill productivity and drive developers mad" was not a design goal for the Graph SDK. That in and on itself would be a reason to look into what happened.

Beyond that, my report showed very clear product issues:

  • What I presume was the REST proxy functionality from 2016 CU3 or similar in other products apparently does not work with the SDK, since I did Mail in version 1.0, and according to the docs, that should've worked even if I had intended to do that
  • there is no obvious indicator the proxying is happening
  • there is no obvious documentation on where to look for an indicator of it happening
  • there is no documentation that mysterious, obviously nonsensical errors might be related to it, causing me to look for such a hypothetical indicator
  • there is no documentation that Graph Explorer shows markedly different behavior from the SDK, which serves to greatly confuse and frustrate an unaware developer and sends them on the wrong debugging path
  • the documentation is either incomplete or too splintered to be readily consumable, and could at least use a general hint of "btw, half the documentation is where you wouldn't look for it"

And based on your response and the like/dislike behavior of the community, what's this community's response? "Hey people, look at this funny angry guy! ... Now go away, we don't talk like that."

No empathy. No care about the actual issue, which, even if you don't consider it a technical issue, is at least a massive UX and "onboarding" issue.

This the beginning of my company's move into the cloud. I have to look into this because we have LOB applications that need to be adapted. The premier way to do so is supposedly Graph, and, under C#/.NET, the Graph SDK. And that presents itself as unreliable, confusingly documented and very time-consuming to develop with.

And your response to that is apathy and ridicule.

I accept the fact that you, personally, don't have to care. I don't need your pity.

But Microsoft as an organization should ponder whether it is truly constructive to make this the first impression people get of developing for Azure, at the beginning of their move to the cloud. If not because they care for product quality or customer satisfaction, at least because it might drive people away to competitors.

bf-ckiendl avatar May 05 '21 07:05 bf-ckiendl

And your response to that is apathy and ridicule.

I accept the fact that you, personally, don't have to care.

You totally got me wrong... my point in using animated gif was to diffuse the anger in your rant. My point: if you want to provide feedback and get someone to help, cursing, attacking, and belittling someone's work isn't the approach. Being constructive works.

andrewconnell avatar May 05 '21 09:05 andrewconnell

Maybe we got off on the wrong foot, and I do acknowledge that I obviously set the tone for this conversation.

However, I do believe you misunderstood my intent as well: At this point, I'm through. I wasted ridiculous amounts of time on this, I figured out what happened and (presumably) why, and I can proceed. Right here, right now, I don't need help anymore.

I consider it important that this information is discoverable, because my own searches were frustratingly fruitless, so I made sure the very simple hint "if Graph complains about its own resource types, it's probably because your mailbox is on premises and you're not actually talking to Graph" can be found when searching for the relevant error messages.

And I am laying it all out for the developers, including the immense amount of frustration it causes, for the developers to decide whether they care.

Because the truth is: This was not just frustrating for me, it also would've been frustratingly easy to prevent. After I had figured out what was happening, after I diagnosed the cause for it, after I read the documentation of the responsible feature, I realized that I did, in fact, get something specific from the Exchange back. When I tried to diagnose the problem, I got an error message, so I investigated the error message. I focused on that which the system said was wrong. At the very end of the message, there's garnish. An additional sentence that says

REST APIs for this mailbox are currently in preview. You can find more information about the preview REST APIs at https://dev.outlook.com.

That seemingly made no sense in context and seemed of no relevance to the actual, pseudo-actionable feedback I got that I was supposedly using a non-existent resource type.

In hindsight, had I googled that sentence, I would've found the relevant documentation of proxies for hybrid deployments. I didn't. Because why would I? It was working fine in Graph Explorer, I was talking to Graph through Graph with Graph, I got a "clear" error message of what supposedly went wrong, so why would I google random boilerplate at the end of the response?

The frustrating part is this: One more sentence would've saved me countless hours of work. Turn

REST APIs for this mailbox are currently in preview. You can find more information about the preview REST APIs at https://dev.outlook.com.

into

This mailbox is hosted on an on-premises server. REST APIs for this mailbox are currently in preview. You can find more information about the preview REST APIs at https://dev.outlook.com.

and it would've been clear from the start. No confusion. No frustration. I would've used another mailbox for testing and everything would've been fine.

It's the same for many of the things I listed. They are tiny things, that, together, cause or could prevent massive amounts of frustration and time wasted.

I don't need help right here, right now.

But I do think the sheer dimension of my frustration is an important part of this issue, because it would've been so easy to prevent. One more sentence in the error boilerplate. One more sentence in the documentation, telling me SDK usage examples are in the HTTP API documentation. One more sentence to warn me that Graph Explorer might behave differently and that its results are not meaningful for the SDK's behavior.

Three sentences of documentation and that entire wall of text would never have happened. And I do believe it is important to have that juxtaposition of cause and effect, to drive home the fact that what may seem like insignificant changes to a seasoned developer makes a world of difference to someone just starting out with Graph SDK.

bf-ckiendl avatar May 05 '21 10:05 bf-ckiendl

@bf-ckiendl I get your frustration, and no one can say you can’t be frustrated. Just know that sometimes people find bugs in open source tools, docs, or even an REST API. Stuff can go wrong, the best we could do is to allow for it to be fixed, right? So create an issue (like you did) and describe the behaviour and the problem. Maybe leave the venting out of it, It’s not constructive. But hey, we’re all humans 🤷🏻‍♂️ Or even create a PR if you have found a solution. We all own the community together, so let’s treat it right

simonagren avatar May 05 '21 17:05 simonagren

I'm going to respond to this, but there are a lot of words to absorb, so please be a little bit patient with me.

darrelmiller avatar May 05 '21 18:05 darrelmiller

@bf-ckiendl, I'm one of the developers targeted by your many colorful words. Thank you for the feedback. I get your frustration. You pointed out many valid issues throughout your missive. It is helpful for us to understand these potential pain points moving LOB applications from on-premise (with EWS I assume) to M365 using Microsoft Graph.

Regarding your form, yeah, it would be more helpful to you and the community to open an issue per problem you find, and to do so without the 💩 . The community and Microsoft can better discover and support the issues when you open the issues on an individual basis. Search indices will index the content better to support discoverability.

So let me see if I got the inventory of your issues right:

  • @odata.type instance annotation being sent by the client and the service API doesn't gracefully ignore it. We plan on removing odata.type instance annotations being set by default on our models as we can't expect the service API to ignore these gracefully. This hid the next issue.
  • When sending a message, you get an error messages mentioning Microsoft.OutlookServices.Message returned by the service API makes no sense and has no actionable information (which you later discovered to be caused by the target mailbox being on-premise). Did you find Use REST APIs to access mailboxes in Exchange hybrid deployments (preview) and have you verified that you environment meets the requirements?
  • Graph Explorer and SDK, when submitting the same request, it does not result in the same outcomes for the same mailbox. This one is unexpected. What was this request?
  • Lack of SDK specific documentation. Yes, the SDK documentation is admittedly sparse and we don't make it clear that there are SDK examples in the reference documentation. We certainly have an opportunity to better describe where to get information about these SDKs.
  • Differences between the HTTP request documentation and the URLs the SDK uses. In your sendmail example, they are both correct forms of the URL per OData spec.
  • Unhelpful error messages. Yep.
  • Graph Explorer doesn't make any changes to the information you submit, there really shouldn't be any difference between Graph Explorer and what you see in the clients as the clients are a rather leaky abstraction of the HTTP API.

I'm curious, what was your first step in your getting started experience?

@andrewconnell @simonagren et al are part of the community and they provide feedback all of the time. They don't use harsh words, yet they do provide critical and constructive feedback. They regularly question the direction of Microsoft and our APIs. They care deeply about the products and the community that helps them get things done.

I'm sorry about your frustrating first experience with Microsoft Graph. That is obviously not our intent.

Now then, I'm going to rename this issue Issues moving LOB app from EWS (on-premise) to Microsoft Graph as I think that will make this post more discoverable. May others learn from our mistakes.

MIchaelMainer avatar May 05 '21 19:05 MIchaelMainer

@MIchaelMainer good morning to you. Please do understand what I said more as an impotent rant towards god than an attempt to go for anyone individually. I am unhappy about my experience, but I assume that's not the responsibility of any particular individual developer.

In terms of opening one issue per issue: Great fan of that. In this particular case, from my perspective, it was a quasi-singular issue: I was trying one particular thing (sending through Graph), and no matter in what direction I moved, it didn't work. Basically, I didn't categorize them as different issues, mentally. But yes, I see your point.

Your assumption about the original/current connection mechanism of the LOB application is, unfortunately, optimistic. No, the previous developer decided the best possible way to connect to an Exchange server was a MAPI connection through Outlook, using the Redemption DLL. (And unencrypted anonymous SMTP for sending.) I do not want to defend that decision, but it is a fact that this application was originally developed when we were on 2003 level on both sides. Maybe it was the only option. I don't know. All I can do is do it differently this time around.

  • When sending a message, you get an error messages mentioning Microsoft.OutlookServices.Message returned by the service API makes no sense and has no actionable information (which you later discovered to be caused by the target mailbox being on-premise). Did you find Use REST APIs to access mailboxes in Exchange hybrid deployments (preview) and have you verified that you environment meets the requirements?

This is the feature I was talking about which I discovered afterwards. After I realized I was not talking to Graph but to our own server, I discovered that documentation and that the boilerplate after the error message matched it. On first glance, I would say we should meet the requirements. Unfortunately, it seems the detail link "On-Premises Architectural Requirements for the REST API" is a dead link, so I can't really tell. Since talking to the local server wasn't intended, nobody tried to make it work and neither am I. It's certainly a nice feature, but it's not in scope and not a goal for this project.

  • Graph Explorer and SDK, when submitting the same request, it does not result in the same outcomes for the same mailbox. This one is unexpected. What was this request?

Basic sending. Outlook Mail -> send an email in Graph Explorer works. Both the demo payload (I have a lot of lunch meeting requests from myself by now) as well as whatever payload I tried in the SDK. I essentially used Graph Explorer as a syntax/sanity checker. Graph SDK threw me weird errors about the resource, so I copy-pasted the payload and the url into Graph Explorer and tried there. I figured if the resource were actually broken, Graph Explorer would tell me. It never had any complaints.

Which, knowing what I know now, makes sense in so far as that the request was, in fact, okay. I was simply routed away from Graph. The difference in behavior seems to be more of a backend one.

  • either Graph Explorer has a better relationship with our local Exchange (different protocol?)
  • or Graph Explorer doesn't make that trip for sending anyway and just drops the Message straight into the SMTP queue, unlike Graph SDK
  • or Graph Explorer cheats and quietly routes everything through some sort of Graph Explorer service mailbox. Like I said: Sending as me, using a cloud-based mailbox as a proxy, works - because it doesn't try to talk to the Exchange.
  • Lack of SDK specific documentation. Yes, the SDK documentation is admittedly sparse and we don't make it clear that there are SDK examples in the reference documentation. We certainly have an opportunity to better describe where to get information about these SDKs.

In general, I also just would've liked a straight up class/method/property overview. (If there is one, I didn't find it.) Many examples in the HTTP documentation are presented without explanation of the individual parts of it, and solutions on the web are often even less documented. And sometimes, if you don't know how to do something, it does help to just look at the menu, essentially, and see what's being offered to you.

For example, query parameters popping up in the examples frequently use helper functions on the request, like .Request().Filter("").Select("").GetAsync(). You have Filter, Select, Expand, OrderBy. Great. The query parameter documentation tells me the endsWith-filter requires $count=true to work. All the examples are URLs. So I'm guessing...should be .Request().Filter("").Count(true).GetAsync() or something like that, right? Nope. No .Count() helper, probably because it would collide with collections' .Count().

So what now?

A straight API overview would've alerted me to the existence of QueryOption and the possible arguments to Request() a lot quicker.

I'm curious, what was your first step in your getting started experience?

It was very much a problem/solution oriented approach.

I need to do e-mail programmatically with Office 365. How do I do that? Microsoft Graph API. Okay. How does that work? REST. There's a Graph Explorer thing to check it out. Okay, looks easy enough. Works. Shouldn't be a problem. What do I have to do to start using it in my application? There's an SDK for .NET. How practical.

Then I followed the SDK link from docs.microsoft.com to the Github repository and worked through the Getting started section.

No hiccup initially - mail retrieval worked fine. (In hindsight, I can tell you that's because I was using the test shared mailbox for retrieval, so I could work without spamming my own mailbox with test mails.)

So by the time I got to sending mails, I already had a working integration for mail retrieval, the API docs said sending is essentially just POSTing to /sendMail, so I figured: Shouldn't be too hard. An hour of work, at max.

...and then my descent into madness began.

bf-ckiendl avatar May 06 '21 09:05 bf-ckiendl

@bf-ckiendl good day to you too. We're all good.

Yes, I was optimistic about EWS. Since 2003 you say, well then it could only be MAPI since WebDAV didn't last.

Here's a link to the blog post about On-Premises Architectural Requirements for the REST API.

To take a step back, does your organization plan on staying with a hybrid deployment of Exchange or will you be migrating all mailboxes to M365? Is the mailbox that you are using hosted in Exchange Online (you can use the Outlook Test Email AutoConfiguration tool if you don't know)? I ask as Microsoft Graph wasn't designed with a hybrid environment in mind, and if you need to support integration to business objects other than Mail, Contacts, and Calendar, you'll want to add this consideration. You'd likely have better experiences the the Exchange-based business objects too.

The only difference between Graph Explorer and the SDK will be headers, and maybe the access token since different approaches were used to acquire the token. They use the same protocol and APIs. The payloads are the same. I can successfully use the client library to send mail. I don't have a hybrid environment to test on. If you were to compare a Fiddler capture of the two requests, what are the differences?

I also just would've liked a straight up class/method/property overview. (If there is one, I didn't find it.)

We don't have it. To be honest, there was a conversation about generating the documentation for the client SDK. I was against it. The main reason is that we found that with client SDK documentation, even the written documentation, tended to have an incredibly long-tail of topics/content that were never visited. From what I recall, out of a docset of a few thousand APIs, we'd find 2-3 topics taking 50% of views, another 10-20 topics getting 40% of views, and then the remaining few thousand topics getting the remaining 10%. A lot of content that wasn't viewed much at all would pollute the search indices and hide good content found in the HTTP reference, blog posts, StackOverflow and GitHub. Now then, since we don't provide the client library reference documentation, we don't do a good job providing guidance on how to use the HTTP reference documentation as a guide for understanding the SDKs. Even though the SDKs are leaky abstractions, they are abstractions, and we need to do a better job there for documentation. We do need better documentation on the core functionality of the SDKs.

On a different note, I highly suggest that you get yourself a sandbox tenant so that you can play around with Microsoft Graph without adulterating your work inbox. https://developer.microsoft.com/en-US/microsoft-365/dev-program

MIchaelMainer avatar May 06 '21 21:05 MIchaelMainer

@MIchaelMainer Sandbox tenant you say? Might I suggest adding that somewhere at the top of the Getting Started guide? 😄 'cause I've been staring at various kinds of Azure/O365 documentation for a year, I've watched a dozen Microsoft Training Day sessions regarding Azure and various aspects of developing for it just two months ago...and I have never heard of this. Not even the two-day-session on developing for Power Platform ever mentioned I could get such a thing.

Thank you very much for that.

The company's goal is to move entirely to Exchange Online. Ops was progressing well with that, and then it turned out several of the LOB applications weren't working quite right with the new environment. My personal mailbox is still on premises, one of the test shared mailboxes at EO.

Graph Explorer

Request

POST https://graph.microsoft.com/v1.0/me/sendMail HTTP/1.1
Host: graph.microsoft.com
Connection: keep-alive
Content-Length: 185
sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
DNT: 1
SdkVersion: GraphExplorer/4.0, graph-js/2.1.0 (featureUsage=7)
sec-ch-ua-mobile: ?0
Authorization: Bearer [redacted]
client-request-id: abaa6eab-04e0-df38-6139-224efbb76667
Content-type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36
Accept: */*
Origin: https://developer.microsoft.com
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://developer.microsoft.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: en,en-US;q=0.9,de-DE;q=0.8,de;q=0.7

{"message":{"subject":"Meet for lunch?","body":{"contentType":"Text","content":"The new cafeteria is open."},"toRecipients":[{"emailAddress":{"address":"▒▒▒▒▒@▒▒▒▒▒.de"}}]}}

Response

HTTP/1.1 202 Accepted
Date: Fri, 07 May 2021 06:01:17 GMT
Cache-Control: private
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location, Preference-Applied, Content-Range, request-id, client-request-id, ReadWriteConsistencyToken, SdkVersion, WWW-Authenticate
Strict-Transport-Security: max-age=31536000
request-id: 93ba7bf8-023d-46cf-988b-6ae8a2b5ad8d
client-request-id: abaa6eab-04e0-df38-6139-224efbb76667
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"West Europe","Slice":"E","Ring":"5","ScaleUnit":"005","RoleInstance":"AM4PEPF0000C53E"}}
Content-Length: 0

Graph SDK

Request

POST https://graph.microsoft.com/v1.0/me/microsoft.graph.sendMail HTTP/1.1
Authorization: bearer [redacted]
FeatureFlag: 00000008
Content-Type: application/json
Host: graph.microsoft.com
Content-Length: 377
Expect: 100-continue

{"Message":{"body":{"content":"The new cafeteria is open.","contentType":"text","@odata.type":"microsoft.graph.itemBody"},"subject":"Meet for lunch?","toRecipients":[{"emailAddress":{"address":"▒▒▒▒▒@▒▒▒▒▒.de","@odata.type":"microsoft.graph.emailAddress"},"@odata.type":"microsoft.graph.recipient"}],"@odata.type":"microsoft.graph.message"},"SaveToSentItems":false}

Response

HTTP/1.1 400 Bad Request
Date: Fri, 07 May 2021 06:16:11 GMT
Content-Type: application/json
Cache-Control: private
Strict-Transport-Security: max-age=31536000
request-id: 612300f8-e2e3-4fb0-ba9c-432ccfa02675
client-request-id: 612300f8-e2e3-4fb0-ba9c-432ccfa02675
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"West Europe","Slice":"E","Ring":"5","ScaleUnit":"003","RoleInstance":"AM1PEPF000057DF"}}
Content-Length: 490

{"error":{"code":"RequestBodyRead","message":"A type named 'microsoft.graph.message' could not be resolved by the model. When a model is available, each type name must resolve to a valid type. REST APIs for this mailbox are currently in preview. You can find more information about the preview REST APIs at https://dev.outlook.com/.","innerError":{"date":"2021-05-07T06:16:11","request-id":"612300f8-e2e3-4fb0-ba9c-432ccfa02675","client-request-id":"612300f8-e2e3-4fb0-ba9c-432ccfa02675"}}}

Exception

Ausnahme ausgelöst: "Microsoft.Graph.ServiceException" in mscorlib.dll
Ausnahme ausgelöst: "Microsoft.Graph.ServiceException" in mscorlib.dll
Ausnahme ausgelöst: "Microsoft.Graph.ServiceException" in mscorlib.dll
Eine Ausnahme vom Typ "Microsoft.Graph.ServiceException" ist in mscorlib.dll aufgetreten, doch wurde diese im Benutzercode nicht verarbeitet.
Code: RequestBodyRead
Message: A type named 'microsoft.graph.message' could not be resolved by the model. When a model is available, each type name must resolve to a valid type. REST APIs for this mailbox are currently in preview. You can find more information about the preview REST APIs at https://dev.outlook.com/.
Inner error:
	AdditionalData:
	date: 2021-05-07T06:16:11
	request-id: 612300f8-e2e3-4fb0-ba9c-432ccfa02675
	client-request-id: 612300f8-e2e3-4fb0-ba9c-432ccfa02675
ClientRequestId: 612300f8-e2e3-4fb0-ba9c-432ccfa02675

The German translates as

Exception triggered

and

An exception of type "Microsoft.Graph.ServiceException" emerged in mscorlib.dll, but was not processed in user code.

In terms of the data I sent, I used the straight Graph Explorer example under Outlook Mail -> send an email, the only modification being my e-mail-address as the recipient. For the SDK, I used the example code from the Graph "send mail" documentation, with the following modifications:

  • I used my own e-mail-address as the recipient
  • I removed the CC recipient, to match Graph Explorer's example
  • I used the service client I had already instantiated, rather than instantiating a new one

I don't know if this is of any relevance, but what sticks out to me is that GE declares SdkVersion: GraphExplorer/4.0, purely for the fact that I'm using 3.30 rather than the 4.0 preview of Graph SDK. Does that refer to the same SDK version, or is Graph Explorer entirely independent?

Edit to add: To preempt a question about the DefaultHttpProvider change exposed by the FeatureFlag: I added that to the service client to set Fiddler as the proxy. That wasn't there for the majority of development.

bf-ckiendl avatar May 07 '21 07:05 bf-ckiendl

You mentioned earlier that you could submit the same request sent from the client via Graph Explorer and it just worked, even with odata.type set for each object. What happens if you use the fully qualified name of microsoft.graph.sendmail method in Graph Explorer?

To be open, I'm grasping here as I don't know what is happening on the backend, and obviously I don't know what is triggering the different behavior. Regarding the LOB application, is it running in the context of a user? It may make sense to add an on-premise mailbox discovery feature so that the LOB app uses the existing MAPI-based functionality when the user's mailbox is hosted on-premise and use the Microsoft Graph based API for user mailboxes that have been migrated to Exchange Online. I'd try one of the properties from the following query to get information about whether a mailbox is on-premise:

https://graph.microsoft.com/v1.0/me?$select=onPremisesDistinguishedName,onPremisesDomainName,onPremisesExtensionAttributes,onPremisesImmutableId,onPremisesLastSyncDateTime,onPremisesSamAccountName,onPremisesSecurityIdentifier,onPremisesUserPrincipalName

You could potentially use EWS AutoDiscover API as well.

Graph Explorer SdkVersion header is different than .NET client SDK SdkVersion header. It is expected.

MIchaelMainer avatar May 07 '21 20:05 MIchaelMainer

You mentioned earlier that you could submit the same request sent from the client via Graph Explorer and it just worked, even with odata.type set for each object. What happens if you use the fully qualified name of microsoft.graph.sendmail method in Graph Explorer?

That's one of the first things I tried because the difference in URLs is the most glaring difference between the two requests. Graph Explorer happily sends mail through https://graph.microsoft.com/v1.0/me/microsoft.graph.sendMail as well. No obvious difference in functionality.

To be open, I'm grasping here as I don't know what is happening on the backend, and obviously I don't know what is triggering the different behavior.

No worries. As I said: The main thing that was annoying to me was that it happened so transparently that it wasn't recognizable as a different/separate issue. I spent a lot of time trying to debug client-side Graph code when the trouble was really Graph⟷Exchange interaction. That's a lot of wasted time.

It seems to me, as a non-MSer, that there's no header info telling the SDK about the backend's behavior either. iow you can't detect whether this is happening properly. It would be sort of an ugly hack, but would it be acceptable for the SDK to abuse the exception's .Message's content as an indicator?

Basically, before throwing Microsoft.Graph.ServiceException, check if .Message.Contains("REST APIs for this mailbox are currently in preview. You can find more information about the preview REST APIs at https://dev.outlook.com/."), and, if true, wrap the whole thing in an outer PotentialHybridExchangeException? Or at least append something to .Message along the lines of "The preceeding sentences indicate Graph attempted an Exchange Hybrid Deployment connection to an on-premises server."?

Just something that screams at the user-developer "this might be an entirely different issue than the error message implies, and it's probably not even an issue with Graph or the SDK".

(Proper solution, obviously, would be for the backend to indicate this behavior through a response header. Ideally with the server name of the server that Graph talked to. Something along the lines of X-MS-Hybrid-Exchange-Connection: ex03.internal.contoso.com. That would also allow devs who are trying to do this on purpose to identify misconfigured servers in a cluster.)

Regarding the LOB application, is it running in the context of a user?

Yep, classic WinForms gray despair. Authenticates towards Azure automagically based on the logged in domain user (Azure AD sync etc.). Since mail retrieval works and I never got a 403 during sending, I'm ruling out authentication and authorization as error sources.

It may make sense to add an on-premise mailbox discovery feature so that the LOB app uses the existing MAPI-based functionality when the user's mailbox is hosted on-premise and use the Microsoft Graph based API for user mailboxes that have been migrated to Exchange Online. I'd try one of the properties from the following query to get information about whether a mailbox is on-premise:

https://graph.microsoft.com/v1.0/me?$select=onPremisesDistinguishedName,onPremisesDomainName,onPremisesExtensionAttributes,onPremisesImmutableId,onPremisesLastSyncDateTime,onPremisesSamAccountName,onPremisesSecurityIdentifier,onPremisesUserPrincipalName

Thank you, that'll come in handy. :smiley:

bf-ckiendl avatar May 10 '21 10:05 bf-ckiendl

It would be sort of an ugly hack, but would it be acceptable for the SDK to abuse the exception's .Message's content as an indicator?

This is a great idea in the case we (my team) owns the service API and client API experience together. We are focused on the client and as part of that trade-off, we made a decision a long time ago that we couldn't scale the mapping of resources' exceptions. We would get it wrong. We try to be a pass through for most exception scenarios.

MIchaelMainer avatar May 10 '21 20:05 MIchaelMainer

We should have documentation covering hybrid and migration scenario.

MIchaelMainer avatar Jul 07 '21 18:07 MIchaelMainer

I am having this same issues in various APIs (including the Mail.Send/Subject issue). The consistent thing is that if I use an access token from Graph Explorer it works. What is different with the Graph Explorer application registration? Why does this application not have the same issues? I've posted other posts similar to this and even logged cases with MS. They just push it off saying hybrid Graph is in beta.

@MIchaelMainer, is there any update on this issue? I have yet to see any straight answer on this.

markdepalma avatar Nov 11 '21 13:11 markdepalma

@markdepalma Can you share a requestId where it fails? The one difference I see is that the SDK sends Expect: 100- Continue but Graph Explorer doesn't.

Could you try turning the Expect header off using this? https://docs.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.expect100continue?view=net-5.0

darrelmiller avatar Nov 11 '21 14:11 darrelmiller

@darrelmiller, I'll collect an example right now.

markdepalma avatar Nov 11 '21 15:11 markdepalma

@darrelmiller ,

I'm doing this straight from Fiddler for ease of replication and am not using an Expect header at all. I'm re-using the Graph Explorer access token in the EXACT same request.

Request with MY app registration's access token:

HTTP/1.1 400 Bad Request
Date: Thu, 11 Nov 2021 15:19:48 GMT
Content-Type: application/json
Cache-Control: private
Vary: Accept-Encoding
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location, Preference-Applied, Content-Range, request-id, client-request-id, ReadWriteConsistencyToken, SdkVersion, WWW-Authenticate
Strict-Transport-Security: max-age=31536000
request-id: a94460d3-e59f-4a0c-89c3-a11a7e3c88f9
client-request-id: a94460d3-e59f-4a0c-89c3-a11a7e3c88f9
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Central US","Slice":"E","Ring":"5","ScaleUnit":"002","RoleInstance":"CH01EPF00009A48"}}
Content-Length: 523


{
	"error": {
		"code": "RequestBodyRead",
		"message": "The property 'subject' does not exist on type 'Microsoft.OutlookServices.Message'. Make sure to only use property names that are defined by the type or mark the type as open type. REST APIs for this mailbox are currently in preview. You can find more information about the preview REST APIs at https://dev.outlook.com/.",
		"innerError": {
			"date": "2021-11-11T15:19:49",
			"request-id": "a94460d3-e59f-4a0c-89c3-a11a7e3c88f9",
			"client-request-id": "a94460d3-e59f-4a0c-89c3-a11a7e3c88f9"
		}
	}
}

SAME request with Graph Explorer's access token:

HTTP/1.1 202 Accepted
Date: Thu, 11 Nov 2021 15:23:03 GMT
Cache-Control: private
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location, Preference-Applied, Content-Range, request-id, client-request-id, ReadWriteConsistencyToken, SdkVersion, WWW-Authenticate
Strict-Transport-Security: max-age=31536000
request-id: 278d7740-a8a4-49ba-872d-ddcaef58659e
client-request-id: 278d7740-a8a4-49ba-872d-ddcaef58659e
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Central US","Slice":"E","Ring":"5","ScaleUnit":"002","RoleInstance":"CH01EPF00009A48"}}
Content-Length: 0

markdepalma avatar Nov 11 '21 15:11 markdepalma

@darrelmiller, were you able to find anything with the request IDs?

markdepalma avatar Nov 15 '21 18:11 markdepalma

@markdepalma Sorry for the delay. I looked at both requests and I don't see any obvious issue. I have forwarded all this information to our Mail team to investigate. The only thing I might suggest is that you try consenting your app to Mail.Send.Shared. That is something that Graph Explorer was consented to that your app wasn't.

darrelmiller avatar Nov 19 '21 17:11 darrelmiller

@markdepalma Sorry for the delay. I looked at both requests and I don't see any obvious issue. I have forwarded all this information to our Mail team to investigate. The only thing I might suggest is that you try consenting your app to Mail.Send.Shared. That is something that Graph Explorer was consented to that your app wasn't.

Thank you, @darrelmiller. I can add that, but it won't do anything. This theme of Graph Explorer's token behaving differently for hybrid Exchange users when compared to an internal application registration plays out with many different examples.

I had this issue here also: https://stackoverflow.com/questions/65013290/graph-api-returning-404-in-certain-data-centers-when-accessing-on-prem-mailbox. In this example I had everything exactly identical with the permissions and had the same issue. The examples that started this thread are the same as well. You can google this and there are a decent amount of other examples. Some of them have come to the same conclusion as I have (like in this thread), but there has never been an acknowledgement or explanation for it from what I've seen.

markdepalma avatar Nov 19 '21 17:11 markdepalma

Closing stale issues as we are now on newer versions.

ddyett avatar Jun 28 '23 01:06 ddyett