diff --git a/server/channels/app/channel.go b/server/channels/app/channel.go index bdff359a25..8893e455a9 100644 --- a/server/channels/app/channel.go +++ b/server/channels/app/channel.go @@ -1842,12 +1842,13 @@ func (a *App) GetChannel(c request.CTX, channelID string) (*model.Channel, *mode func (s *Server) getChannel(c request.CTX, channelID string) (*model.Channel, *model.AppError) { channel, err := s.Store().Channel().Get(channelID, true) if err != nil { + errCtx := map[string]any{"channel_id": channelID} var nfErr *store.ErrNotFound switch { case errors.As(err, &nfErr): - return nil, model.NewAppError("GetChannel", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(err) + return nil, model.NewAppError("GetChannel", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(err) default: - return nil, model.NewAppError("GetChannel", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + return nil, model.NewAppError("GetChannel", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(err) } } return channel, nil @@ -1856,12 +1857,13 @@ func (s *Server) getChannel(c request.CTX, channelID string) (*model.Channel, *m func (a *App) GetChannels(c request.CTX, channelIDs []string) ([]*model.Channel, *model.AppError) { channels, err := a.Srv().Store().Channel().GetMany(channelIDs, true) if err != nil { + errCtx := map[string]any{"channel_id": channelIDs} var nfErr *store.ErrNotFound switch { case errors.As(err, &nfErr): - return nil, model.NewAppError("GetChannel", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(err) + return nil, model.NewAppError("GetChannel", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(err) default: - return nil, model.NewAppError("GetChannel", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + return nil, model.NewAppError("GetChannel", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(err) } } return channels, nil @@ -2370,12 +2372,13 @@ func (a *App) LeaveChannel(c request.CTX, channelID string, userID string) *mode cresult := <-sc if cresult.NErr != nil { + errCtx := map[string]any{"channel_id": channelID} var nfErr *store.ErrNotFound switch { case errors.As(cresult.NErr, &nfErr): - return model.NewAppError("LeaveChannel", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(cresult.NErr) + return model.NewAppError("LeaveChannel", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(cresult.NErr) default: - return model.NewAppError("LeaveChannel", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(cresult.NErr) + return model.NewAppError("LeaveChannel", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(cresult.NErr) } } uresult := <-uc diff --git a/server/channels/app/command.go b/server/channels/app/command.go index cf50f8f917..0dee719456 100644 --- a/server/channels/app/command.go +++ b/server/channels/app/command.go @@ -421,12 +421,13 @@ func (a *App) tryExecuteCustomCommand(c request.CTX, args *model.CommandArgs, tr cr := <-chanChan if cr.NErr != nil { + errCtx := map[string]any{"channel_id": args.ChannelId} var nfErr *store.ErrNotFound switch { case errors.As(cr.NErr, &nfErr): - return nil, nil, model.NewAppError("tryExecuteCustomCommand", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(cr.NErr) + return nil, nil, model.NewAppError("tryExecuteCustomCommand", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(cr.NErr) default: - return nil, nil, model.NewAppError("tryExecuteCustomCommand", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(cr.NErr) + return nil, nil, model.NewAppError("tryExecuteCustomCommand", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(cr.NErr) } } channel := cr.Data diff --git a/server/channels/app/group.go b/server/channels/app/group.go index 35d70c4ccd..31157bec5d 100644 --- a/server/channels/app/group.go +++ b/server/channels/app/group.go @@ -370,12 +370,13 @@ func (a *App) UpsertGroupSyncable(groupSyncable *model.GroupSyncable) (*model.Gr if groupSyncable.Type == model.GroupSyncableTypeChannel { channel, nErr := a.Srv().Store().Channel().Get(groupSyncable.SyncableId, true) if nErr != nil { + errCtx := map[string]any{"channel_id": groupSyncable.SyncableId} var nfErr *store.ErrNotFound switch { case errors.As(nErr, &nfErr): - return nil, model.NewAppError("UpsertGroupSyncable", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(nErr) + return nil, model.NewAppError("UpsertGroupSyncable", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(nErr) default: - return nil, model.NewAppError("UpsertGroupSyncable", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + return nil, model.NewAppError("UpsertGroupSyncable", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(nErr) } } diff --git a/server/channels/app/integration_action.go b/server/channels/app/integration_action.go index 0db5339ccd..9afd2a6d19 100644 --- a/server/channels/app/integration_action.go +++ b/server/channels/app/integration_action.go @@ -107,12 +107,13 @@ func (a *App) DoPostActionWithCookie(c request.CTX, postID, actionId, userID, se channel, err := a.Srv().Store().Channel().Get(cookie.ChannelId, true) if err != nil { + errCtx := map[string]any{"channel_id": cookie.ChannelId} var nfErr *store.ErrNotFound switch { case errors.As(err, &nfErr): - return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(err) + return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(err) default: - return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(err) } } diff --git a/server/channels/app/post.go b/server/channels/app/post.go index e2169539e6..c56595012a 100644 --- a/server/channels/app/post.go +++ b/server/channels/app/post.go @@ -118,12 +118,13 @@ func (a *App) CreatePostAsUser(c request.CTX, post *model.Post, currentSessionId func (a *App) CreatePostMissingChannel(c request.CTX, post *model.Post, triggerWebhooks bool, setOnline bool) (*model.Post, *model.AppError) { channel, err := a.Srv().Store().Channel().Get(post.ChannelId, true) if err != nil { + errCtx := map[string]any{"channel_id": post.ChannelId} var nfErr *store.ErrNotFound switch { case errors.As(err, &nfErr): - return nil, model.NewAppError("CreatePostMissingChannel", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(err) + return nil, model.NewAppError("CreatePostMissingChannel", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(err) default: - return nil, model.NewAppError("CreatePostMissingChannel", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + return nil, model.NewAppError("CreatePostMissingChannel", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(err) } } @@ -573,7 +574,7 @@ func (a *App) handlePostEvents(c request.CTX, post *model.Post, user *model.User if triggerWebhooks { a.Srv().Go(func() { if err := a.handleWebhookEvents(c, post, team, channel, user); err != nil { - c.Logger().Error("Failed to handle webhook event", mlog.Err(err)) + c.Logger().Error("Failed to handle webhook event", mlog.String("user_id", user.Id), mlog.String("post_id", post.Id), mlog.Err(err)) } }) } diff --git a/server/channels/app/webhook.go b/server/channels/app/webhook.go index 79425dacef..2990bd985c 100644 --- a/server/channels/app/webhook.go +++ b/server/channels/app/webhook.go @@ -95,15 +95,16 @@ func (a *App) handleWebhookEvents(c request.CTX, post *model.Post, team *model.T } func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayload, hook *model.OutgoingWebhook, post *model.Post, channel *model.Channel) { + logger := c.Logger().With(mlog.String("outgoing_webhook_id", hook.Id), mlog.String("post_id", post.Id), mlog.String("channel_id", channel.Id), mlog.String("content_type", hook.ContentType)) + var jsonBytes []byte var err error - contentType := "application/x-www-form-urlencoded" if hook.ContentType == "application/json" { contentType = "application/json" jsonBytes, err = json.Marshal(payload) if err != nil { - c.Logger().Warn("Failed to encode to JSON", mlog.Err(err)) + logger.Warn("Failed to encode to JSON", mlog.Err(err)) return } } @@ -131,14 +132,14 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa if a.Config().ServiceSettings.EnableOutgoingOAuthConnections != nil && *a.Config().ServiceSettings.EnableOutgoingOAuthConnections && a.OutgoingOAuthConnections() != nil { connection, err := a.OutgoingOAuthConnections().GetConnectionForAudience(c, url) if err != nil { - c.Logger().Error("Failed to find an outgoing oauth connection for the webhook", mlog.Err(err)) + logger.Error("Failed to find an outgoing oauth connection for the webhook", mlog.Err(err)) return } if connection != nil { accessToken, err = a.OutgoingOAuthConnections().RetrieveTokenForConnection(c, connection) if err != nil { - c.Logger().Error("Failed to retrieve token for outgoing oauth connection", mlog.Err(err)) + logger.Error("Failed to retrieve token for outgoing oauth connection", mlog.Err(err)) return } } @@ -147,9 +148,9 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa webhookResp, err := a.doOutgoingWebhookRequest(url, body, contentType, accessToken) if err != nil { if errors.Is(err, context.DeadlineExceeded) { - c.Logger().Error("Outgoing Webhook POST timed out. Consider increasing ServiceSettings.OutgoingIntegrationRequestsTimeout.", mlog.Err(err)) + logger.Error("Outgoing Webhook POST timed out. Consider increasing ServiceSettings.OutgoingIntegrationRequestsTimeout.", mlog.Err(err)) } else { - c.Logger().Error("Outgoing Webhook POST failed", mlog.Err(err)) + logger.Error("Outgoing Webhook POST failed", mlog.Err(err)) } return } @@ -181,7 +182,7 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa webhookResp.IconURL = hook.IconURL } if _, err := a.CreateWebhookPost(c, hook.CreatorId, channel, text, webhookResp.Username, webhookResp.IconURL, "", webhookResp.Props, webhookResp.Type, postRootId, webhookResp.Priority); err != nil { - c.Logger().Error("Failed to create response post.", mlog.Err(err)) + logger.Error("Failed to create response post.", mlog.Err(err)) } } }() @@ -528,12 +529,13 @@ func (a *App) CreateOutgoingWebhook(hook *model.OutgoingWebhook) (*model.Outgoin if hook.ChannelId != "" { channel, errCh := a.Srv().Store().Channel().Get(hook.ChannelId, true) if errCh != nil { + errCtx := map[string]any{"channel_id": hook.ChannelId} var nfErr *store.ErrNotFound switch { case errors.As(errCh, &nfErr): - return nil, model.NewAppError("CreateOutgoingWebhook", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(errCh) + return nil, model.NewAppError("CreateOutgoingWebhook", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(errCh) default: - return nil, model.NewAppError("CreateOutgoingWebhook", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(errCh) + return nil, model.NewAppError("CreateOutgoingWebhook", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(errCh) } } @@ -780,7 +782,7 @@ func (a *App) HandleIncomingWebhook(c request.CTX, hookID string, req *model.Inc if channelName[0] == '@' { result, nErr := a.Srv().Store().User().GetByUsername(channelName[1:]) if nErr != nil { - return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.user.app_error", nil, "", http.StatusBadRequest).Wrap(nErr) + return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.user.app_error", map[string]any{"user": channelName[1:]}, "", http.StatusBadRequest).Wrap(nErr) } ch, err := a.GetOrCreateDirectChannel(c, hook.UserId, result.Id) if err != nil { @@ -806,12 +808,13 @@ func (a *App) HandleIncomingWebhook(c request.CTX, hookID string, req *model.Inc var err error channel, err = a.Srv().Store().Channel().Get(hook.ChannelId, true) if err != nil { + errCtx := map[string]any{"channel_id": hook.ChannelId} var nfErr *store.ErrNotFound switch { case errors.As(err, &nfErr): - return model.NewAppError("HandleIncomingWebhook", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(err) + return model.NewAppError("HandleIncomingWebhook", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(err) default: - return model.NewAppError("HandleIncomingWebhook", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + return model.NewAppError("HandleIncomingWebhook", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(err) } } } @@ -831,16 +834,16 @@ func (a *App) HandleIncomingWebhook(c request.CTX, hookID string, req *model.Inc } if hook.ChannelLocked && hook.ChannelId != channel.Id { - return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.channel_locked.app_error", nil, "", http.StatusForbidden) + return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.channel_locked.app_error", map[string]any{"channel_id": channel.Id}, "", http.StatusForbidden) } resultU := <-uchan if resultU.NErr != nil { - return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.user.app_error", nil, "", http.StatusForbidden).Wrap(resultU.NErr) + return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.user.app_error", map[string]any{"user": hook.UserId}, "", http.StatusForbidden).Wrap(resultU.NErr) } if channel.Type != model.ChannelTypeOpen && !a.HasPermissionToChannel(c, hook.UserId, channel.Id, model.PermissionReadChannelContent) { - return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.permissions.app_error", nil, "", http.StatusForbidden) + return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.permissions.app_error", map[string]any{"user": hook.UserId, "channel": channel.Id}, "", http.StatusForbidden) } overrideUsername := hook.Username @@ -891,7 +894,7 @@ func (a *App) HandleCommandWebhook(c request.CTX, hookID string, response *model var nfErr *store.ErrNotFound switch { case errors.As(nErr, &nfErr): - return model.NewAppError("HandleCommandWebhook", "app.command_webhook.get.missing", map[string]any{"hook_id": hookID}, "", http.StatusNotFound).Wrap(nErr) + return model.NewAppError("HandleCommandWebhook", "app.command_webhook.get.missing", nil, "", http.StatusNotFound).Wrap(nErr) default: return model.NewAppError("HandleCommandWebhook", "app.command_webhook.get.internal_error", nil, "", http.StatusInternalServerError).Wrap(nErr) } @@ -904,7 +907,7 @@ func (a *App) HandleCommandWebhook(c request.CTX, hookID string, response *model case errors.As(cmdErr, &appErr): return appErr default: - return model.NewAppError("HandleCommandWebhook", "web.command_webhook.command.app_error", nil, "", http.StatusBadRequest).Wrap(cmdErr) + return model.NewAppError("HandleCommandWebhook", "web.command_webhook.command.app_error", map[string]any{"command_id": hook.CommandId}, "", http.StatusBadRequest).Wrap(cmdErr) } } diff --git a/server/channels/web/webhook.go b/server/channels/web/webhook.go index 18ec4c671d..ac511f3913 100644 --- a/server/channels/web/webhook.go +++ b/server/channels/web/webhook.go @@ -25,10 +25,11 @@ func (w *Web) InitWebhooks() { func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] + errCtx := map[string]any{"hook_id": id} r.ParseForm() - var err *model.AppError + var appErr *model.AppError var mediaType string incomingWebhookPayload := &model.IncomingWebhookRequest{} contentType := r.Header.Get("Content-Type") @@ -37,12 +38,7 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { var mimeErr error mediaType, _, mimeErr = mime.ParseMediaType(contentType) if mimeErr != nil && mimeErr != mime.ErrInvalidMediaParameter { - c.Err = model.NewAppError("incomingWebhook", - "api.webhook.incoming.error", - nil, - "webhook_id="+id+", error: "+mimeErr.Error(), - http.StatusBadRequest, - ) + c.Err = model.NewAppError("incomingWebhook", "web.incoming_webhook.media_type.app_error", errCtx, "", http.StatusBadRequest).Wrap(mimeErr) return } } @@ -63,12 +59,13 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { } }() + errCtx["media_type"] = mediaType if mediaType == "application/x-www-form-urlencoded" { payload := strings.NewReader(r.FormValue("payload")) - incomingWebhookPayload, err = decodePayload(payload) - if err != nil { - c.Err = err + incomingWebhookPayload, appErr = decodePayload(payload) + if appErr != nil { + c.Err = model.NewAppError("incomingWebhook", "web.incoming_webhook.decode.app_error", errCtx, "", http.StatusBadRequest).Wrap(appErr) return } } else if mediaType == "multipart/form-data" { @@ -78,25 +75,20 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { err := decoder.Decode(incomingWebhookPayload, r.PostForm) if err != nil { - c.Err = model.NewAppError("incomingWebhook", - "api.webhook.incoming.error", - nil, - "webhook_id="+id+", error: "+err.Error(), - http.StatusBadRequest, - ) + c.Err = model.NewAppError("incomingWebhook", "web.incoming_webhook.decode.app_error", errCtx, "", http.StatusBadRequest).Wrap(err) return } } else { - incomingWebhookPayload, err = decodePayload(r.Body) - if err != nil { - c.Err = err + incomingWebhookPayload, appErr = decodePayload(r.Body) + if appErr != nil { + c.Err = model.NewAppError("incomingWebhook", "web.incoming_webhook.decode.app_error", errCtx, "", appErr.StatusCode).Wrap(appErr) return } } - err = c.App.HandleIncomingWebhook(c.AppContext, id, incomingWebhookPayload) - if err != nil { - c.Err = err + appErr = c.App.HandleIncomingWebhook(c.AppContext, id, incomingWebhookPayload) + if appErr != nil { + c.Err = model.NewAppError("incomingWebhook", "web.incoming_webhook.general.app_error", errCtx, "", appErr.StatusCode).Wrap(appErr) return } @@ -107,16 +99,17 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] + errCtx := map[string]any{"hook_id": id} response, err := model.CommandResponseFromHTTPBody(r.Header.Get("Content-Type"), r.Body) if err != nil { - c.Err = model.NewAppError("commandWebhook", "web.command_webhook.parse.app_error", nil, "", http.StatusBadRequest).Wrap(err) + c.Err = model.NewAppError("commandWebhook", "web.command_webhook.parse.app_error", errCtx, "", http.StatusBadRequest).Wrap(err) return } appErr := c.App.HandleCommandWebhook(c.AppContext, id, response) if appErr != nil { - c.Err = appErr + c.Err = model.NewAppError("commandWebhook", "web.command_webhook.general.app_error", errCtx, "", appErr.StatusCode).Wrap(appErr) return } diff --git a/server/cmd/mmctl/commands/channel_e2e_test.go b/server/cmd/mmctl/commands/channel_e2e_test.go index 66ea65b23e..16622ba0c1 100644 --- a/server/cmd/mmctl/commands/channel_e2e_test.go +++ b/server/cmd/mmctl/commands/channel_e2e_test.go @@ -357,7 +357,7 @@ func (s *MmctlE2ETestSuite) TestDeleteChannelsCmd() { _, err = s.th.App.GetChannel(s.th.Context, channel.Id) s.Require().NotNil(err) - s.Require().Equal(fmt.Sprintf("GetChannel: Unable to find the existing channel., resource \"Channel\" not found, id: %s", channel.Id), err.Error()) + s.CheckErrorID(err, "app.channel.get.existing.app_error") }) s.Run("Delete channel without permissions", func() { @@ -401,7 +401,7 @@ func (s *MmctlE2ETestSuite) TestDeleteChannelsCmd() { s.Require().Nil(channel) s.Require().NotNil(err) - s.Require().Equal(fmt.Sprintf("GetChannel: Unable to find the existing channel., resource \"Channel\" not found, id: %s", notExistingChannelID), err.Error()) + s.CheckErrorID(err, "app.channel.get.existing.app_error") }) } diff --git a/server/cmd/mmctl/commands/mmctl_test.go b/server/cmd/mmctl/commands/mmctl_test.go index 37492c6f1a..e44dd8f4b9 100644 --- a/server/cmd/mmctl/commands/mmctl_test.go +++ b/server/cmd/mmctl/commands/mmctl_test.go @@ -96,3 +96,7 @@ func (s *MmctlE2ETestSuite) RunForAllClients(testName string, fn func(client.Cli fn(s.th.LocalClient) }) } + +func (s *MmctlE2ETestSuite) CheckErrorID(err error, errorId string) { + api4.CheckErrorID(s.T(), err, errorId) +} diff --git a/server/i18n/en.json b/server/i18n/en.json index 9c034429b3..6bc4c6d588 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -4382,10 +4382,6 @@ "id": "api.webhook.create_outgoing.triggers.app_error", "translation": "Either trigger_words or channel_id must be set." }, - { - "id": "api.webhook.incoming.error", - "translation": "Could not decode the multipart payload of incoming webhook." - }, { "id": "api.webhook.team_mismatch.app_error", "translation": "Unable to update webhook across teams." @@ -4580,11 +4576,11 @@ }, { "id": "app.channel.get.existing.app_error", - "translation": "Unable to find the existing channel." + "translation": "Unable to find the existing channel {{.channel_id}}." }, { "id": "app.channel.get.find.app_error", - "translation": "We encountered an error finding the channel." + "translation": "We encountered an error finding the channel {{.channel_id}}." }, { "id": "app.channel.get_all.app_error", @@ -10270,11 +10266,15 @@ }, { "id": "web.command_webhook.command.app_error", - "translation": "Couldn't find the command." + "translation": "Couldn't find the command {{.command_id}}." + }, + { + "id": "web.command_webhook.general.app_error", + "translation": "Failed to handle command webhook {{.hook_id}}." }, { "id": "web.command_webhook.parse.app_error", - "translation": "Unable to parse incoming data." + "translation": "Unable to parse incoming data for webhook {{.hook_id}}." }, { "id": "web.error.unsupported_browser.browser_get_latest.chrome", @@ -10382,23 +10382,35 @@ }, { "id": "web.incoming_webhook.channel_locked.app_error", - "translation": "This webhook is not permitted to post to the requested channel." + "translation": "This webhook is not permitted to post to the requested channel {{.channel_id}}" + }, + { + "id": "web.incoming_webhook.decode.app_error", + "translation": "Failed to decode the payload of media type {{.media_type}} for incoming webhook {{.hook_id}}." }, { "id": "web.incoming_webhook.disabled.app_error", "translation": "Incoming webhooks have been disabled by the system admin." }, + { + "id": "web.incoming_webhook.general.app_error", + "translation": "Failed to handle the payload of media type {{.media_type}} for incoming webhook {{.hook_id}}." + }, { "id": "web.incoming_webhook.invalid.app_error", "translation": "Invalid webhook." }, + { + "id": "web.incoming_webhook.media_type.app_error", + "translation": "Failed to parse media incoming webhook {{.hook_id}}." + }, { "id": "web.incoming_webhook.parse.app_error", "translation": "Unable to parse incoming data." }, { "id": "web.incoming_webhook.permissions.app_error", - "translation": "Inappropriate channel permissions." + "translation": "User {{.user}} does not have appropriate permissions to channel {{.channel}}" }, { "id": "web.incoming_webhook.split_props_length.app_error", @@ -10410,6 +10422,6 @@ }, { "id": "web.incoming_webhook.user.app_error", - "translation": "Couldn't find the user." + "translation": "Couldn't find the user {{.user}}" } ]