4

I need to change the default "Sorry, my bot code is having an issue" exception message. It seems like it is a somewhat complicated process. I have tried to do as it says in this blog post: http://wp.sjkp.dk/change-the-sorry-my-bot-code-is-having-an-issue-in-microsoft-bot-framework/

I don't have knowledge of dependency injection or inversion of control, so this is becoming quite a challenge. I'm using the Bot Builder version 3.5.5.

This is the code from the blog, which I tried to use on my bot:

The PostUnhandledExceptionToUser class:

using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Internals;
using Microsoft.Bot.Builder.Internals.Fibers;
using Microsoft.Bot.Connector;
using System;
using System.Diagnostics;
using System.Net.Mime;
using System.Resources;
using System.Threading;
using System.Threading.Tasks;

namespace Tiimo.Bot.BotFramework
{
    public class PostUnhandledExceptionToUser : IPostToBot
    {
        private readonly ResourceManager resources;
        private readonly IPostToBot inner;
        private readonly IBotToUser botToUser;
        private readonly TraceListener trace;

        public PostUnhandledExceptionToUser(IPostToBot inner, IBotToUser botToUser, ResourceManager resources, TraceListener trace)
        {
            SetField.NotNull(out this.inner, nameof(inner), inner);
            SetField.NotNull(out this.botToUser, nameof(botToUser), botToUser);
            SetField.NotNull(out this.resources, nameof(resources), resources);
            SetField.NotNull(out this.trace, nameof(trace), trace);
        }

        async Task IPostToBot.PostAsync(IActivity activity, CancellationToken token)
        {
            try
            {
                await this.inner.PostAsync(activity, token);
            }
            catch (Exception error)
            {
                try
                {
                    if (Debugger.IsAttached)
                    {
                        var message = this.botToUser.MakeMessage();
                        message.Text = $"Exception: { error.Message}";
                        message.Attachments = new[]
                        {
                            new Attachment(contentType: MediaTypeNames.Text.Plain, content: error.StackTrace)
                        };

                        await this.botToUser.PostAsync(message);
                    }
                    else
                    {
                        await this.botToUser.PostAsync("My Personal Error Message");
                    }
                }
                catch (Exception inner)
                {
                    this.trace.WriteLine(inner);
                }

                throw;
            }
        }
    }
}

The DefaultExceptionMessageOverrideModule class:

using Autofac;
using Autofac.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Internals;
using Microsoft.Bot.Builder.History;
using Microsoft.Bot.Builder.Internals.Fibers;
using Microsoft.Bot.Builder.Scorables.Internals;
using Microsoft.Bot.Connector;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Resources;
using System.Web;


namespace Tiimo.Bot.BotFramework
{
    public class DefaultExceptionMessageOverrideModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<PostUnhandledExceptionToUser>().Keyed<IPostToBot>(typeof(PostUnhandledExceptionToUser)).InstancePerLifetimeScope();


            RegisterAdapterChain<IPostToBot>(builder, 
                typeof(PersistentDialogTask),
                typeof(ExceptionTranslationDialogTask),
                typeof(SerializeByConversation),
                typeof(SetAmbientThreadCulture),
                typeof(PostUnhandledExceptionToUser),
                typeof(LogPostToBot)
            )
            .InstancePerLifetimeScope();
        }

        public static IRegistrationBuilder<TLimit, SimpleActivatorData, SingleRegistrationStyle> RegisterAdapterChain<TLimit>(ContainerBuilder builder, params Type[] types)
        {
            return
                builder
                .Register(c =>
                {
                // http://stackoverflow.com/questions/23406641/how-to-mix-decorators-in-autofac
                TLimit service = default(TLimit);
                    for (int index = 0; index < types.Length; ++index)
                    {
                    // resolve the keyed adapter, passing the previous service as the inner parameter
                    service = c.ResolveKeyed<TLimit>(types[index], TypedParameter.From(service));
                    }

                    return service;
                })
                .As<TLimit>();
        }
    }  
}

After creating these classes I went to the Global.asax file and put this code:

var builder = new ContainerBuilder();
builder.RegisterModule(new DefaultExceptionMessageOverrideModule());
builder.Update(Conversation.Container);

It builds successfully, however when I send a message to the bot I get the following error:

An exception was thrown while invoking the constructor ‘Void .ctor(Microsoft.Bot.Builder.Dialogs.Internals.IPostToBot, Microsoft.Bot.Builder.Dialogs.Internals.IBotData)’ on type ‘PersistentDialogTask’. —> Valor não pode ser nulo. Nome do parâmetro: inner (See inner exception for details.)

Which I guess it translates to something like

An exception was thrown while invoking the constructor ‘Void .ctor(Microsoft.Bot.Builder.Dialogs.Internals.IPostToBot, Microsoft.Bot.Builder.Dialogs.Internals.IBotData)’ on type ‘PersistentDialogTask’. —> Value cannot be null. Parameter name: inner (See inner exception for details.)

Is there something wrong with the code? What am I doing wrong? Is there another way to do this?

Ezequiel Jadib
  • 14,767
  • 2
  • 38
  • 43
adamasan
  • 1,092
  • 1
  • 12
  • 33
  • Possible duplicate of [How to avoid "Sorry, my bot code is having an issue" in Microsoft Bot Framework](http://stackoverflow.com/questions/39609638/how-to-avoid-sorry-my-bot-code-is-having-an-issue-in-microsoft-bot-framework) – Dan Esparza Mar 23 '17 at 12:43
  • 3
    @DanEsparza I don't think so. That user want's to avoid it, I want to change it. There's some differences. – adamasan Mar 23 '17 at 14:54

1 Answers1

3

Try using this code for registering the adapter chain:

builder
        .RegisterAdapterChain<IPostToBot>
        (
            typeof(EventLoopDialogTask),
            typeof(SetAmbientThreadCulture),
            typeof(PersistentDialogTask),
            typeof(ExceptionTranslationDialogTask),
            typeof(SerializeByConversation),
            typeof(PostUnhandledExceptionToUser),
            typeof(LogPostToBot)
        )
        .InstancePerLifetimeScope();

I believe that at some point the PersistentDialogTask was updated and they added a dependency on an IPostToBot implementation which, in the registration you were using, was not being provided. I took the updated code from the DialogModule.

If you go over the code you will see that the EventLoopDialogTask is the first item in the chain and it doesn't depend on any IPostToBot implementation. My guess is that at the time of the post you read, the PersisteDialogTask was the first item in that chain.

Ezequiel Jadib
  • 14,767
  • 2
  • 38
  • 43
  • It is working great now! I spent way too much time trying to do this, so thank you so much. :) – adamasan Mar 23 '17 at 14:58
  • How does this solve your issue? Which one of those types resolved this? – Dan Esparza Mar 23 '17 at 15:58
  • Fair question. I believe the problem was that the Adapter chain changed during some point and now the PersistentDialogTask depends on an IPostToBot implementation (see [code](https://github.com/Microsoft/BotBuilder/blob/master/CSharp/Library/Microsoft.Bot.Builder/Dialogs/DialogTask.cs#L448), which with the registration he was using it wasn't provided. I updated the answer with those details. – Ezequiel Jadib Mar 23 '17 at 16:23