diff --git a/src/Gargantext/API/Admin/Auth/Types.hs b/src/Gargantext/API/Admin/Auth/Types.hs
index 9350aa0e6effd011b90e313c425d4b9d667283c2..fb5faaebca64ad5ceb96dcfed45cf37ef0c5eefa 100644
--- a/src/Gargantext/API/Admin/Auth/Types.hs
+++ b/src/Gargantext/API/Admin/Auth/Types.hs
@@ -65,6 +65,7 @@ instance FromJWT AuthenticatedUser
 data AuthenticationError
   = LoginFailed NodeId UserId Jose.Error
   | InvalidUsernameOrPassword
+  | UserNotAuthorized UserId Text
   deriving (Show, Eq)
 
 -- TODO-SECURITY why is the CookieSettings necessary?
diff --git a/src/Gargantext/API/Errors.hs b/src/Gargantext/API/Errors.hs
index 10d0bc60ac985209bdddd49089295a58996636a4..f0c58686048acc62e84ed332eee0c975838db038 100644
--- a/src/Gargantext/API/Errors.hs
+++ b/src/Gargantext/API/Errors.hs
@@ -12,6 +12,7 @@ module Gargantext.API.Errors (
   -- * Conversion functions
   , backendErrorToFrontendError
   , frontendErrorToServerError
+  , frontendErrorToGQLServerError
 
   -- * Temporary shims
   , showAsServantJSONErr
@@ -20,6 +21,10 @@ module Gargantext.API.Errors (
 import Prelude
 
 import Control.Exception
+import Data.Aeson qualified as JSON
+import Data.Text qualified as T
+import Data.Text.Lazy qualified as TL
+import Data.Text.Lazy.Encoding qualified as TE
 import Data.Validity ( prettyValidation )
 import Gargantext.API.Admin.Auth.Types
 import Gargantext.API.Errors.Class as Class
@@ -28,20 +33,19 @@ import Gargantext.API.Errors.Types as Types
 import Gargantext.Database.Query.Table.Node.Error hiding (nodeError)
 import Gargantext.Database.Query.Tree hiding (treeError)
 import Gargantext.Utils.Jobs.Monad (JobError(..))
+import Network.HTTP.Types.Status qualified as HTTP
 import Servant.Server
-import qualified Data.Aeson as JSON
-import qualified Data.Text as T
-import qualified Network.HTTP.Types.Status as HTTP
-import qualified Data.Text.Lazy.Encoding as TE
-import qualified Data.Text.Lazy as TL
 
 $(deriveHttpStatusCode ''BackendErrorCode)
 
 data GargErrorScheme
   = -- | The old error scheme.
     GES_old
-    -- | The new error scheme, that returns a 'FrontendError'.
+  -- | The new error scheme, that returns a 'FrontendError'.
   | GES_new
+  -- | Error scheme for GraphQL, has to be slightly different
+  --   {errors: [{message, extensions: { ... }}]}
+  --   https://spec.graphql.org/June2018/#sec-Errors
     deriving (Show, Eq)
 
 -- | Transforms a backend internal error into something that the frontend
@@ -49,26 +53,56 @@ data GargErrorScheme
 -- as we later encode this into a 'ServerError' in the main server handler.
 backendErrorToFrontendError :: BackendInternalError -> FrontendError
 backendErrorToFrontendError = \case
-  InternalNodeError nodeError
-    -> nodeErrorToFrontendError nodeError
-  InternalTreeError treeError
-    -> treeErrorToFrontendError treeError
-  InternalValidationError validationError
-    -> mkFrontendErr' "A validation error occurred"
-         $ FE_validation_error $ case prettyValidation validationError of
-             Nothing -> "unknown_validation_error"
-             Just v  -> T.pack v
   InternalAuthenticationError authError
     -> authErrorToFrontendError authError
-  InternalServerError internalServerError
-    -> internalServerErrorToFrontendError internalServerError
+  InternalNodeError nodeError
+    -> nodeErrorToFrontendError nodeError
   InternalJobError jobError
     -> jobErrorToFrontendError jobError
+  InternalServerError internalServerError
+    -> internalServerErrorToFrontendError internalServerError
+  InternalTreeError treeError
+    -> treeErrorToFrontendError treeError
   -- As this carries a 'SomeException' which might exposes sensible
   -- information, we do not send to the frontend its content.
   InternalUnexpectedError _
     -> let msg = T.pack $ "An unexpected error occurred. Please check your server logs."
        in mkFrontendErr' msg $ FE_internal_server_error msg
+  InternalValidationError validationError
+    -> mkFrontendErr' "A validation error occurred"
+         $ FE_validation_error $ case prettyValidation validationError of
+             Nothing -> "unknown_validation_error"
+             Just v  -> T.pack v
+
+frontendErrorToGQLServerError :: FrontendError -> ServerError
+frontendErrorToGQLServerError fe@(FrontendError diag ty _) =
+  ServerError { errHTTPCode     = HTTP.statusCode $ backendErrorTypeToErrStatus ty
+              , errReasonPhrase = T.unpack diag
+              , errBody         = JSON.encode (GraphQLError fe)
+              , errHeaders      = mempty
+              }
+
+authErrorToFrontendError :: AuthenticationError -> FrontendError
+authErrorToFrontendError = \case
+  -- For now, we ignore the Jose error, as they are too specific
+  -- (i.e. they should be logged internally to Sentry rather than shared
+  -- externally).
+  LoginFailed nid uid _
+    -> mkFrontendErr' "Invalid username/password, or invalid session token." $ FE_login_failed_error nid uid
+  InvalidUsernameOrPassword
+    -> mkFrontendErr' "Invalid username or password." $ FE_login_failed_invalid_username_or_password
+  UserNotAuthorized uId msg
+    -> mkFrontendErr' "User not authorized. " $ FE_user_not_authorized uId msg
+
+-- | Converts a 'FrontendError' into a 'ServerError' that the servant app can
+-- return to the frontend.
+frontendErrorToServerError :: FrontendError -> ServerError
+frontendErrorToServerError fe@(FrontendError diag ty _) =
+  ServerError { errHTTPCode     = HTTP.statusCode $ backendErrorTypeToErrStatus ty
+              , errReasonPhrase = T.unpack diag
+              , errBody         = JSON.encode fe
+              , errHeaders      = mempty
+              }
 
 internalServerErrorToFrontendError :: ServerError -> FrontendError
 internalServerErrorToFrontendError = \case
@@ -86,16 +120,6 @@ jobErrorToFrontendError = \case
   UnknownJob jobId   -> mkFrontendErrNoDiagnostic $ FE_job_unknown_job jobId
   JobException err   -> mkFrontendErrNoDiagnostic $ FE_job_generic_exception (T.pack $ displayException err)
 
-authErrorToFrontendError :: AuthenticationError -> FrontendError
-authErrorToFrontendError = \case
-  -- For now, we ignore the Jose error, as they are too specific
-  -- (i.e. they should be logged internally to Sentry rather than shared
-  -- externally).
-  LoginFailed nid uid _
-    -> mkFrontendErr' "Invalid username/password, or invalid session token." $ FE_login_failed_error nid uid
-  InvalidUsernameOrPassword
-    -> mkFrontendErr' "Invalid username or password." $ FE_login_failed_invalid_username_or_password
-
 nodeErrorToFrontendError :: NodeError -> FrontendError
 nodeErrorToFrontendError ne = case ne of
   NoListFound lid
@@ -147,16 +171,6 @@ treeErrorToFrontendError te = case te of
   EmptyRoot          -> mkFrontendErrShow FE_tree_empty_root
   TooManyRoots roots -> mkFrontendErrShow $ FE_tree_too_many_roots roots
 
--- | Converts a 'FrontendError' into a 'ServerError' that the servant app can
--- return to the frontend.
-frontendErrorToServerError :: FrontendError -> ServerError
-frontendErrorToServerError fe@(FrontendError diag ty _) =
-  ServerError { errHTTPCode     = HTTP.statusCode $ backendErrorTypeToErrStatus ty
-              , errReasonPhrase = T.unpack diag
-              , errBody         = JSON.encode fe
-              , errHeaders      = mempty
-              }
-
 showAsServantJSONErr :: BackendInternalError -> ServerError
 showAsServantJSONErr (InternalNodeError err@(NoListFound {}))  = err404 { errBody = JSON.encode err }
 showAsServantJSONErr (InternalNodeError err@NoRootFound{})     = err404 { errBody = JSON.encode err }
diff --git a/src/Gargantext/API/Errors/Types.hs b/src/Gargantext/API/Errors/Types.hs
index 1334985dba97bb58ec0fa600084e46966dc01b14..c8aa1e82ac7bd16ad06eb648eadff597a718a58e 100644
--- a/src/Gargantext/API/Errors/Types.hs
+++ b/src/Gargantext/API/Errors/Types.hs
@@ -20,6 +20,7 @@ module Gargantext.API.Errors.Types (
   -- * The internal backend type and an enumeration of all possible backend error types
   , BackendErrorCode(..)
   , BackendInternalError(..)
+  , GraphQLError(..)
   , ToFrontendErrorData(..)
 
   -- * Constructing frontend errors
@@ -37,35 +38,33 @@ module Gargantext.API.Errors.Types (
 
 import Control.Exception
 import Control.Lens (makePrisms)
-import Data.Aeson as JSON
+import Control.Monad.Fail (fail)
+import Data.Aeson (FromJSON(..), ToJSON(..), Value(..), (.:), (.=), object, withObject, toJSON)
 import Data.Aeson.Types (typeMismatch, emptyArray)
+import Data.List.NonEmpty qualified as NE
 import Data.Singletons.TH
-import Data.List.NonEmpty (NonEmpty)
+import Data.Text qualified as T
 import Data.Typeable
 import Data.Validity (Validation(..), ValidationChain (..), prettyValidation)
 import GHC.Generics
 import GHC.Stack
+import Gargantext.API.Admin.Auth.Types (AuthenticationError)
 import Gargantext.API.Errors.Class
 import Gargantext.API.Errors.TH
 import Gargantext.API.Errors.Types.Backend
 import Gargantext.Core.Types (HasValidationError(..))
+import Gargantext.Core.Types.Individu
 import Gargantext.Database.Admin.Types.Node
 import Gargantext.Database.Query.Table.Node.Error
 import Gargantext.Database.Query.Tree.Error
+import Gargantext.Prelude hiding (Location, WithStacktrace)
 import Gargantext.Utils.Dict
-import Prelude
+import Gargantext.Utils.Jobs.Monad qualified as Jobs
 import Servant (ServerError)
 import Servant.Job.Core
+import Servant.Job.Types qualified as SJ
 import Test.QuickCheck
 import Test.QuickCheck.Instances.Text ()
-import qualified Data.Text as T
-import qualified Gargantext.Utils.Jobs.Monad as Jobs
-import qualified Servant.Job.Types as SJ
-import Text.Read (readMaybe)
-import qualified Data.List.NonEmpty as NE
-import Data.Maybe
-import Gargantext.API.Admin.Auth.Types (AuthenticationError)
-import Gargantext.Core.Types.Individu
 
 -- | A 'WithStacktrace' carries an error alongside its
 -- 'CallStack', to be able to print the correct source location
@@ -83,14 +82,18 @@ instance Exception e => Exception (WithStacktrace e) where
 -------------------------------------------------------------------
 -- | An internal error which can be emitted from the backend and later
 -- converted into a 'FrontendError', for later consumption.
+
+
+
+
 data BackendInternalError
-  = InternalNodeError           !NodeError
-  | InternalTreeError           !TreeError
-  | InternalValidationError     !Validation
-  | InternalAuthenticationError !AuthenticationError
-  | InternalServerError         !ServerError
+  = InternalAuthenticationError !AuthenticationError
   | InternalJobError            !Jobs.JobError
+  | InternalNodeError           !NodeError
+  | InternalServerError         !ServerError
+  | InternalTreeError           !TreeError
   | InternalUnexpectedError     !SomeException
+  | InternalValidationError     !Validation
   deriving (Show, Typeable)
 
 makePrisms ''BackendInternalError
@@ -265,6 +268,12 @@ data instance ToFrontendErrorData 'EC_403__login_failed_invalid_username_or_pass
   deriving (Show, Eq, Generic)
 
 
+data instance ToFrontendErrorData 'EC_403__user_not_authorized =
+  FE_user_not_authorized { una_user_id :: UserId
+                         , una_msg     :: T.Text }
+  deriving (Show, Eq, Generic)
+
+
 --
 -- Tree errors
 --
@@ -326,34 +335,29 @@ data instance ToFrontendErrorData 'EC_405__not_allowed =
 
 instance ToJSON (ToFrontendErrorData 'EC_404__node_list_not_found) where
   toJSON (FE_node_list_not_found lid) =
-    JSON.object [ "list_id" .= toJSON lid ]
-
+    object [ "list_id" .= toJSON lid ]
 instance FromJSON (ToFrontendErrorData 'EC_404__node_list_not_found) where
   parseJSON = withObject "FE_node_list_not_found" $ \o -> do
     lnf_list_id <- o .: "list_id"
     pure FE_node_list_not_found{..}
 
 instance ToJSON (ToFrontendErrorData 'EC_404__node_root_not_found) where
-  toJSON _ = JSON.Null
-
+  toJSON _ = Null
 instance FromJSON (ToFrontendErrorData 'EC_404__node_root_not_found) where
   parseJSON _ = pure FE_node_root_not_found
 
 instance ToJSON (ToFrontendErrorData 'EC_404__node_corpus_not_found) where
-  toJSON _ = JSON.Null
-
+  toJSON _ = Null
 instance FromJSON (ToFrontendErrorData 'EC_404__node_corpus_not_found) where
   parseJSON _ = pure FE_node_corpus_not_found
-
+  
 instance ToJSON (ToFrontendErrorData 'EC_500__node_not_implemented_yet) where
-  toJSON _ = JSON.Null
-
+  toJSON _ = Null
 instance FromJSON (ToFrontendErrorData 'EC_500__node_not_implemented_yet) where
   parseJSON _ = pure FE_node_not_implemented_yet
-
+  
 instance ToJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_not_found) where
   toJSON (FE_node_lookup_failed_not_found nodeId) = object [ "node_id" .= toJSON nodeId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_not_found) where
   parseJSON = withObject "FE_node_lookup_failed_not_found" $ \o -> do
     nenf_node_id <- o .: "node_id"
@@ -361,7 +365,6 @@ instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_not_found) wh
 
 instance ToJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_parent_not_found) where
   toJSON (FE_node_lookup_failed_parent_not_found nodeId) = object [ "node_id" .= toJSON nodeId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_parent_not_found) where
   parseJSON = withObject "FE_node_lookup_failed_parent_not_found" $ \o -> do
     nepnf_node_id <- o .: "node_id"
@@ -369,7 +372,6 @@ instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_parent_not_fo
 
 instance ToJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_user_not_found) where
   toJSON (FE_node_lookup_failed_user_not_found userId) = object [ "user_id" .= toJSON userId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_user_not_found) where
   parseJSON = withObject "FE_node_lookup_failed_user_not_found" $ \o -> do
     nenf_user_id <- o .: "user_id"
@@ -377,7 +379,6 @@ instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_user_not_foun
 
 instance ToJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_username_not_found) where
   toJSON (FE_node_lookup_failed_username_not_found username) = object [ "username" .= toJSON username ]
-
 instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_username_not_found) where
   parseJSON = withObject "FE_node_lookup_failed_username_not_found" $ \o -> do
     nenf_username <- o .: "username"
@@ -385,7 +386,6 @@ instance FromJSON (ToFrontendErrorData 'EC_404__node_lookup_failed_username_not_
 
 instance ToJSON (ToFrontendErrorData 'EC_400__node_creation_failed_user_negative_id) where
   toJSON (FE_node_creation_failed_user_negative_id userId) = object [ "user_id" .= toJSON userId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_user_negative_id) where
   parseJSON = withObject "FE_node_creation_failed_user_negative_id" $ \o -> do
     neuni_user_id <- o .: "user_id"
@@ -394,7 +394,6 @@ instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_user_negati
 instance ToJSON (ToFrontendErrorData 'EC_400__node_lookup_failed_user_too_many_roots) where
   toJSON (FE_node_lookup_failed_user_too_many_roots userId roots) =
     object [ "user_id" .= toJSON userId, "roots" .= toJSON roots ]
-
 instance FromJSON (ToFrontendErrorData 'EC_400__node_lookup_failed_user_too_many_roots) where
   parseJSON = withObject "FE_node_lookup_failed_user_too_many_roots" $ \o -> do
     netmr_user_id <- o .: "user_id"
@@ -403,7 +402,6 @@ instance FromJSON (ToFrontendErrorData 'EC_400__node_lookup_failed_user_too_many
 
 instance ToJSON (ToFrontendErrorData 'EC_404__node_context_not_found) where
   toJSON (FE_node_context_not_found cId) = object [ "context_id" .= toJSON cId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_404__node_context_not_found) where
   parseJSON = withObject "FE_node_context_not_found" $ \o -> do
     necnf_context_id <- o .: "context_id"
@@ -411,7 +409,6 @@ instance FromJSON (ToFrontendErrorData 'EC_404__node_context_not_found) where
 
 instance ToJSON (ToFrontendErrorData 'EC_400__node_creation_failed_no_parent) where
   toJSON (FE_node_creation_failed_no_parent uId) = object [ "user_id" .= toJSON uId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_no_parent) where
   parseJSON = withObject "FE_node_creation_failed_no_parent" $ \o -> do
     necnp_user_id <- o .: "user_id"
@@ -420,7 +417,6 @@ instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_no_parent)
 instance ToJSON (ToFrontendErrorData 'EC_400__node_creation_failed_parent_exists) where
   toJSON FE_node_creation_failed_parent_exists{..} =
     object [ "user_id" .= toJSON necpe_user_id, "parent_id" .= toJSON necpe_parent_id ]
-
 instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_parent_exists) where
   parseJSON = withObject "FE_node_creation_failed_parent_exists" $ \o -> do
     necpe_user_id   <- o .: "user_id"
@@ -429,8 +425,7 @@ instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_parent_exis
 
 instance ToJSON (ToFrontendErrorData 'EC_400__node_creation_failed_insert_node) where
   toJSON FE_node_creation_failed_insert_node{..} =
-    JSON.object [ "user_id" .= toJSON necin_user_id, "parent_id" .= necin_parent_id ]
-
+    object [ "user_id" .= toJSON necin_user_id, "parent_id" .= necin_parent_id ]
 instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_insert_node) where
   parseJSON = withObject "FE_node_creation_failed_insert_node" $ \o -> do
     necin_user_id   <- o .: "user_id"
@@ -439,16 +434,14 @@ instance FromJSON (ToFrontendErrorData 'EC_400__node_creation_failed_insert_node
 
 instance ToJSON (ToFrontendErrorData 'EC_500__node_generic_exception) where
   toJSON FE_node_generic_exception{..} =
-    JSON.object [ "error" .= nege_error ]
-
+    object [ "error" .= nege_error ]
 instance FromJSON (ToFrontendErrorData 'EC_500__node_generic_exception) where
   parseJSON = withObject "FE_node_generic_exception" $ \o -> do
     nege_error <- o .: "error"
     pure FE_node_generic_exception{..}
 
 instance ToJSON (ToFrontendErrorData 'EC_400__node_needs_configuration) where
-  toJSON _ = JSON.Null
-
+  toJSON _ = Null
 instance FromJSON (ToFrontendErrorData 'EC_400__node_needs_configuration) where
   parseJSON _ = pure FE_node_needs_configuration
 
@@ -458,7 +451,6 @@ instance FromJSON (ToFrontendErrorData 'EC_400__node_needs_configuration) where
 
 instance ToJSON (ToFrontendErrorData 'EC_400__validation_error) where
   toJSON (FE_validation_error val) = toJSON val
-
 instance FromJSON (ToFrontendErrorData 'EC_400__validation_error) where
   parseJSON (String txt) = pure $ FE_validation_error txt
   parseJSON ty           = typeMismatch "FE_validation_error" ty
@@ -470,7 +462,6 @@ instance FromJSON (ToFrontendErrorData 'EC_400__validation_error) where
 instance ToJSON (ToFrontendErrorData 'EC_403__login_failed_error) where
   toJSON FE_login_failed_error{..} =
     object [ "user_id" .= toJSON lfe_user_id, "node_id" .= toJSON lfe_node_id ]
-
 instance FromJSON (ToFrontendErrorData 'EC_403__login_failed_error) where
   parseJSON = withObject "FE_login_failed_error" $ \o -> do
     lfe_user_id <- o .: "user_id"
@@ -481,18 +472,25 @@ instance FromJSON (ToFrontendErrorData 'EC_403__login_failed_error) where
 instance ToJSON (ToFrontendErrorData 'EC_403__login_failed_invalid_username_or_password) where
   toJSON FE_login_failed_invalid_username_or_password =
     object []
-
 instance FromJSON (ToFrontendErrorData 'EC_403__login_failed_invalid_username_or_password) where
   parseJSON = withObject "FE_login_failed_invalid_username_or_password" $ \_o -> do
     pure FE_login_failed_invalid_username_or_password
 
+instance ToJSON (ToFrontendErrorData 'EC_403__user_not_authorized) where
+  toJSON FE_user_not_authorized { .. } =
+    object [ "user_id" .= toJSON una_user_id, "msg" .= toJSON una_msg ]
+instance FromJSON (ToFrontendErrorData 'EC_403__user_not_authorized) where
+  parseJSON = withObject "FE_user_not_authorized" $ \o -> do
+    una_user_id <- o .: "user_id"
+    una_msg     <- o .: "msg"
+    pure FE_user_not_authorized { .. }
+
 --
 -- internal server errors
 --
 
 instance ToJSON (ToFrontendErrorData 'EC_500__internal_server_error) where
   toJSON FE_internal_server_error{..} = object [ "error" .= toJSON ise_error ]
-
 instance FromJSON (ToFrontendErrorData 'EC_500__internal_server_error) where
   parseJSON = withObject "FE_internal_server_error" $ \o -> do
     ise_error <- o .: "error"
@@ -500,7 +498,6 @@ instance FromJSON (ToFrontendErrorData 'EC_500__internal_server_error) where
 
 instance ToJSON (ToFrontendErrorData 'EC_405__not_allowed) where
   toJSON FE_not_allowed{..} = object [ "error" .= toJSON isena_error ]
-
 instance FromJSON (ToFrontendErrorData 'EC_405__not_allowed) where
   parseJSON = withObject "FE_not_allowed" $ \o -> do
     isena_error <- o .: "error"
@@ -512,21 +509,18 @@ instance FromJSON (ToFrontendErrorData 'EC_405__not_allowed) where
 --
 
 instance ToJSON (ToFrontendErrorData 'EC_404__tree_root_not_found) where
-  toJSON _ = JSON.Null
-
+  toJSON _ = Null
 instance FromJSON (ToFrontendErrorData 'EC_404__tree_root_not_found) where
   parseJSON _ = pure FE_tree_root_not_found
 
 instance ToJSON (ToFrontendErrorData 'EC_404__tree_empty_root) where
-  toJSON _ = JSON.Null
-
+  toJSON _ = Null
 instance FromJSON (ToFrontendErrorData 'EC_404__tree_empty_root) where
   parseJSON _ = pure FE_tree_empty_root
 
 instance ToJSON (ToFrontendErrorData 'EC_500__tree_too_many_roots) where
   toJSON (FE_tree_too_many_roots roots) =
     object [ "node_ids" .= NE.toList roots ]
-
 instance FromJSON (ToFrontendErrorData 'EC_500__tree_too_many_roots) where
   parseJSON = withObject "FE_tree_too_many_roots" $ \o -> do
     tmr_roots <- o .: "node_ids"
@@ -539,7 +533,6 @@ instance FromJSON (ToFrontendErrorData 'EC_500__tree_too_many_roots) where
 instance ToJSON (ToFrontendErrorData 'EC_500__job_invalid_id_type) where
   toJSON (FE_job_invalid_id_type idTy) =
     object [ "type" .= toJSON idTy ]
-
 instance FromJSON (ToFrontendErrorData 'EC_500__job_invalid_id_type) where
   parseJSON = withObject "FE_job_invalid_id_type" $ \o -> do
     jeiit_type <- o .: "type"
@@ -548,7 +541,6 @@ instance FromJSON (ToFrontendErrorData 'EC_500__job_invalid_id_type) where
 instance ToJSON (ToFrontendErrorData 'EC_500__job_expired) where
   toJSON (FE_job_expired jobId) =
     object [ "job_id" .= toJSON jobId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_500__job_expired) where
   parseJSON = withObject "FE_job_expired" $ \o -> do
     jee_job_id <- o .: "job_id"
@@ -557,7 +549,6 @@ instance FromJSON (ToFrontendErrorData 'EC_500__job_expired) where
 instance ToJSON (ToFrontendErrorData 'EC_500__job_invalid_mac) where
   toJSON (FE_job_invalid_mac mac) =
     object [ "mac" .= toJSON mac ]
-
 instance FromJSON (ToFrontendErrorData 'EC_500__job_invalid_mac) where
   parseJSON = withObject "FE_job_invalid_mac" $ \o -> do
     jeim_mac <- o .: "mac"
@@ -566,7 +557,6 @@ instance FromJSON (ToFrontendErrorData 'EC_500__job_invalid_mac) where
 instance ToJSON (ToFrontendErrorData 'EC_500__job_unknown_job) where
   toJSON (FE_job_unknown_job jobId) =
     object [ "job_id" .= toJSON jobId ]
-
 instance FromJSON (ToFrontendErrorData 'EC_500__job_unknown_job) where
   parseJSON = withObject "FE_job_unknown_job" $ \o -> do
     jeuj_job_id <- o .: "job_id"
@@ -575,7 +565,6 @@ instance FromJSON (ToFrontendErrorData 'EC_500__job_unknown_job) where
 instance ToJSON (ToFrontendErrorData 'EC_500__job_generic_exception) where
   toJSON (FE_job_generic_exception err) =
     object [ "error" .= toJSON err ]
-
 instance FromJSON (ToFrontendErrorData 'EC_500__job_generic_exception) where
   parseJSON = withObject "FE_job_generic_exception" $ \o -> do
     jege_error <- o .: "error"
@@ -656,6 +645,12 @@ genFrontendErr be = do
       -> do
             pure $ mkFrontendErr' txt $ FE_login_failed_invalid_username_or_password
 
+    EC_403__user_not_authorized
+      -> do
+            uid <- arbitrary
+            msg <- arbitrary
+            pure $ mkFrontendErr' txt $ FE_user_not_authorized uid msg
+
     -- internal error
     EC_500__internal_server_error
       -> do err <- arbitrary
@@ -692,7 +687,7 @@ genFrontendErr be = do
             pure $ mkFrontendErr' txt $ FE_job_generic_exception err
 
 instance ToJSON BackendErrorCode where
-  toJSON = JSON.String . T.pack . show
+  toJSON = String . T.pack . show
 
 instance FromJSON BackendErrorCode where
   parseJSON (String s) = case readMaybe (T.unpack s) of
@@ -702,10 +697,10 @@ instance FromJSON BackendErrorCode where
 
 instance ToJSON FrontendError where
   toJSON (FrontendError diag ty dt) =
-    JSON.object [ "diagnostic" .= toJSON diag
-                , "type"       .= toJSON ty
-                , "data"       .= toJSON dt
-                ]
+    object [ "diagnostic" .= toJSON diag
+           , "type"       .= toJSON ty
+           , "data"       .= toJSON dt
+           ]
 
 instance FromJSON FrontendError where
   parseJSON = withObject "FrontendError" $ \o -> do
@@ -775,6 +770,10 @@ instance FromJSON FrontendError where
         (fe_data :: ToFrontendErrorData 'EC_403__login_failed_invalid_username_or_password) <- o .: "data"
         pure FrontendError{..}
 
+      EC_403__user_not_authorized -> do
+        (fe_data :: ToFrontendErrorData 'EC_403__user_not_authorized) <- o .: "data"
+        pure FrontendError{..}
+
       -- internal server error
       EC_500__internal_server_error -> do
         (fe_data :: ToFrontendErrorData 'EC_500__internal_server_error) <- o .: "data"
@@ -810,3 +809,26 @@ instance FromJSON FrontendError where
       EC_500__job_generic_exception -> do
         (fe_data :: ToFrontendErrorData 'EC_500__job_generic_exception) <- o .: "data"
         pure FrontendError{..}
+
+
+
+
+----------------
+--- GraphQL Errors are just FrontendError wrapped in
+-- { error: { message, extensions: { ... } } }
+-- (see https://spec.graphql.org/June2018/#sec-Errors)
+
+newtype GraphQLError = GraphQLError FrontendError
+deriving instance Show GraphQLError
+deriving instance Eq GraphQLError
+instance ToJSON GraphQLError where
+  toJSON (GraphQLError fe@(FrontendError diag _ty _dt)) =
+    object [ "errors" .= toJSON [ object [ "message" .= toJSON diag
+                                         , "extensions" .= toJSON fe ] ] ]
+instance FromJSON GraphQLError where
+  parseJSON = withObject "GraphQLError" $ \o -> do
+    errors <- o .: "errors"
+    fe <- case errors of
+      [] -> fail "No errors provided"
+      (x:_) -> withObject "FrontendError" (\fo -> fo .: "extensions") x
+    pure $ GraphQLError fe
diff --git a/src/Gargantext/API/Errors/Types/Backend.hs b/src/Gargantext/API/Errors/Types/Backend.hs
index 92d7c14f25491acb158b3d1037cb7e9462d6237c..2e00cfd1dc1ba9e45b4e27c959132ed54c6e02d0 100644
--- a/src/Gargantext/API/Errors/Types/Backend.hs
+++ b/src/Gargantext/API/Errors/Types/Backend.hs
@@ -37,6 +37,7 @@ data BackendErrorCode
   -- authentication errors
   | EC_403__login_failed_error
   | EC_403__login_failed_invalid_username_or_password
+  | EC_403__user_not_authorized
   -- tree errors
   | EC_404__tree_root_not_found
   | EC_404__tree_empty_root
diff --git a/src/Gargantext/API/GraphQL/Team.hs b/src/Gargantext/API/GraphQL/Team.hs
index e3d36f870a1e5d7b089c4b07dd038b1e69da3b54..6c834453adae5c4a2c97fde707eaf12ebdc138a7 100644
--- a/src/Gargantext/API/GraphQL/Team.hs
+++ b/src/Gargantext/API/GraphQL/Team.hs
@@ -16,6 +16,7 @@ module Gargantext.API.GraphQL.Team where
 
 import Data.Morpheus.Types (GQLType, ResolverM)
 import Data.Text qualified as T
+import Gargantext.API.Admin.Auth.Types (AuthenticationError(..))
 import Gargantext.API.Admin.Types (HasSettings)
 import Gargantext.API.Errors.Types
 import Gargantext.API.GraphQL.Types (GqlM)
@@ -86,10 +87,12 @@ deleteTeamMembership TeamDeleteMArgs { token, shared_folder_id, team_node_id } =
     [] -> panicTrace $ "[deleteTeamMembership] User with id " <> T.pack (show $ uId teamNode) <> " doesn't exist."
     (( _, node_u):_) -> do
       testAuthUser <- lift $ authUser (nId node_u) token
-      case testAuthUser of
-        Invalid -> panicTrace "[deleteTeamMembership] failed to validate user"
+      lift $ case testAuthUser of
+        -- Invalid -> panicTrace "[deleteTeamMembership] failed to validate user"
+        Invalid -> do
+          throwError $ InternalAuthenticationError $ UserNotAuthorized (uId node_u) "This user is not team owner"
         Valid -> do
-          lift $ deleteMemberShip [(UnsafeMkNodeId shared_folder_id, UnsafeMkNodeId team_node_id)]
+          deleteMemberShip [(UnsafeMkNodeId shared_folder_id, UnsafeMkNodeId team_node_id)]
   where
     uId Node { _node_user_id } = _node_user_id
     nId Node { _node_id } = _node_id
diff --git a/src/Gargantext/API/GraphQL/Utils.hs b/src/Gargantext/API/GraphQL/Utils.hs
index dee31a397a6fd0d87945e92d4c4ee440a89db12e..189ee64b1addf3c15a4f268dd07b86fb8d7e9e7b 100644
--- a/src/Gargantext/API/GraphQL/Utils.hs
+++ b/src/Gargantext/API/GraphQL/Utils.hs
@@ -10,6 +10,7 @@ Portability : POSIX
 
 module Gargantext.API.GraphQL.Utils where
 
+import Control.Lens ((^.))
 import Control.Lens.Getter (view)
 import Data.Morpheus.Types (GQLTypeOptions, fieldLabelModifier)
 import Data.Text qualified as T
@@ -20,7 +21,6 @@ import Gargantext.Database.Admin.Types.Node (NodeId)
 import Gargantext.Database.Prelude (Cmd')
 import Gargantext.Prelude
 import Servant.Auth.Server (verifyJWT, JWTSettings)
-import Control.Lens ((^.))
 
 unPrefix :: T.Text -> GQLTypeOptions -> GQLTypeOptions
 unPrefix prefix options = options { fieldLabelModifier = nflm }
diff --git a/src/Gargantext/API/Server.hs b/src/Gargantext/API/Server.hs
index 376993c178f20fb54d314bf05ae9a18b66be7086..7a57b7c48b8a6235221ad2720552833687a53f42 100644
--- a/src/Gargantext/API/Server.hs
+++ b/src/Gargantext/API/Server.hs
@@ -67,13 +67,16 @@ server env = do
      :<|> hoistServerWithContext
             (Proxy :: Proxy GraphQL.API)
             (Proxy :: Proxy AuthContext)
-            (transformJSON errScheme)
+            (transformJSONGQL errScheme)
             GraphQL.api
      :<|> frontEndServer
   where
     transformJSON :: forall a. GargErrorScheme -> GargM Env BackendInternalError a -> Handler a
     transformJSON GES_old = Handler . withExceptT showAsServantJSONErr . (`runReaderT` env) . logPanicErrors
     transformJSON GES_new = Handler . withExceptT (frontendErrorToServerError . backendErrorToFrontendError) . (`runReaderT` env) . handlePanicErrors
+    transformJSONGQL :: forall a. GargErrorScheme -> GargM Env BackendInternalError a -> Handler a
+    transformJSONGQL GES_old = Handler . withExceptT showAsServantJSONErr . (`runReaderT` env) . logPanicErrors
+    transformJSONGQL GES_new = Handler . withExceptT (frontendErrorToGQLServerError . backendErrorToFrontendError) . (`runReaderT` env) . handlePanicErrors
 
 handlePanicErrors :: GargM Env BackendInternalError a -> GargM Env BackendInternalError a
 handlePanicErrors h = h `catch` handleSomeException
@@ -104,3 +107,4 @@ logPanicErrors h = h `catch` handleSomeException
       = throwError ber -- re-throw the uncaught exception via the 'MonadError' instance
       | otherwise
       = throwM se -- re-throw the uncaught exception.
+