Repository: Nerzal/gocloak
Pull request #146 (2020-02-20 22:01:29)

Error refactoring
Wrap Errors and use APIError struct to also provide the httpstatus code.

Adresses: #132 



Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 6af7df1..f3814ef 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -3,8 +3,8 @@ on:
   - push
   - pull_request
 env:
-  LINT_VERSION: "v1.22.2"
-  NANCY_VERSION: "v0.0.39"
+  LINT_VERSION: "v1.23.6"
+  NANCY_VERSION: "v0.0.41"
 jobs:
   tests:
     runs-on: ubuntu-latest
diff --git a/README.md b/README.md
index 0381c34..ed4c06a 100644
--- a/README.md
+++ b/README.md
@@ -255,10 +255,10 @@ docker run -d \
 	-e KEYCLOAK_USER=admin \
 	-e KEYCLOAK_PASSWORD=secret \
 	-e KEYCLOAK_IMPORT=/tmp/gocloak-realm.json \
-	-v `pwd`/testdata/gocloak-realm.json:/tmp/gocloak-realm.json \
+	-v "`pwd`/testdata/gocloak-realm.json:/tmp/gocloak-realm.json" \
 	-p 8080:8080 \
 	--name gocloak-test \
-	quay.io/keycloak/keycloak
+	quay.io/keycloak/keycloak:latest -Dkeycloak.profile.feature.upload_scripts=enabled
 
 go test
 ```
diff --git a/client.go b/client.go
index d703588..cfea5c7 100644
--- a/client.go
+++ b/client.go
@@ -2,7 +2,6 @@ package gocloak
 
 import (
 	"encoding/base64"
-	"errors"
 	"fmt"
 	"net/url"
 	"strings"
@@ -12,6 +11,7 @@ import (
 	"github.com/Nerzal/gocloak/v4/pkg/jwx"
 	"github.com/dgrijalva/jwt-go"
 	"github.com/go-resty/resty/v2"
+	"github.com/pkg/errors"
 )
 
 type gocloak struct {
@@ -58,18 +58,27 @@ func (client *gocloak) getRequestWithBasicAuth(clientID string, clientSecret str
 		httpBasicAuth := base64.URLEncoding.EncodeToString([]byte(clientID + ":" + clientSecret))
 		req.SetHeader("Authorization", "Basic "+httpBasicAuth)
 	}
+
 	return req
 }
 
-func checkForError(resp *resty.Response, err error) error {
+func checkForError(resp *resty.Response, err error, errMessage string) error {
 	if err != nil {
-		return err
+		return &APIError{
+			Code:    0,
+			Message: errMessage,
+		}
 	}
+
 	if resp == nil {
-		return errors.New("empty response")
+		return &APIError{
+			Message: "empty response",
+		}
 	}
+
 	if resp.IsError() {
 		var msg string
+
 		e := resp.Error().(*HTTPErrorResponse)
 		if e != nil && len(e.ErrorMessage) > 0 {
 			msg = fmt.Sprintf("%s: %s", resp.Status(), e.ErrorMessage)
@@ -78,11 +87,13 @@ func checkForError(resp *resty.Response, err error) error {
 		} else {
 			msg = resp.Status()
 		}
-		if resp.StatusCode() == 409 {
-			return &ObjectAlreadyExists{ErrorMessage: msg}
+
+		return &APIError{
+			Code:    resp.StatusCode(),
+			Message: msg,
 		}
-		return errors.New(msg)
 	}
+
 	return nil
 }
 
@@ -137,12 +148,14 @@ func (client *gocloak) getAdminRealmURL(realm string, path ...string) string {
 }
 
 func (client *gocloak) GetServerInfo(accessToken string) (*ServerInfoRepesentation, error) {
+	var errMessage = "could not get server info"
 	var result ServerInfoRepesentation
+
 	resp, err := client.getRequestWithBearerAuth(accessToken).
 		SetResult(&result).
 		Get(makeURL(client.basePath, "auth", "admin", "serverinfo"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -151,12 +164,14 @@ func (client *gocloak) GetServerInfo(accessToken string) (*ServerInfoRepesentati
 
 // GetUserInfo calls the UserInfo endpoint
 func (client *gocloak) GetUserInfo(accessToken string, realm string) (*UserInfo, error) {
+	const errMessage = "could not get user info"
+
 	var result UserInfo
 	resp, err := client.getRequestWithBearerAuth(accessToken).
 		SetResult(&result).
 		Get(client.getRealmURL(realm, openIDConnect, "userinfo"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -164,12 +179,14 @@ func (client *gocloak) GetUserInfo(accessToken string, realm string) (*UserInfo,
 }
 
 func (client *gocloak) getNewCerts(realm string) (*CertResponse, error) {
+	const errMessage = "could not get newCerts"
+
 	var result CertResponse
 	resp, err := client.getRequest().
 		SetResult(&result).
 		Get(client.getRealmURL(realm, openIDConnect, "certs"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -178,15 +195,20 @@ func (client *gocloak) getNewCerts(realm string) (*CertResponse, error) {
 
 // GetCerts fetches certificates for the given realm from the public /open-id-connect/certs endpoint
 func (client *gocloak) GetCerts(realm string) (*CertResponse, error) {
+	const errMessage = "could not get certs"
+
 	client.certsLock.Lock()
 	defer client.certsLock.Unlock()
+
 	if cert, ok := client.certsCache[realm]; ok {
 		return cert, nil
 	}
+
 	cert, err := client.getNewCerts(realm)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
+
 	client.certsCache[realm] = cert
 
 	timer := time.NewTimer(client.Config.CertsInvalidateTime)
@@ -196,17 +218,20 @@ func (client *gocloak) GetCerts(realm string) (*CertResponse, error) {
 		delete(client.certsCache, realm)
 		client.certsLock.Unlock()
 	}()
+
 	return cert, nil
 }
 
 // GetIssuer gets the issuer of the given realm
 func (client *gocloak) GetIssuer(realm string) (*IssuerResponse, error) {
+	const errMessage = "could not get issuer"
+
 	var result IssuerResponse
 	resp, err := client.getRequest().
 		SetResult(&result).
 		Get(client.getRealmURL(realm))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -215,6 +240,8 @@ func (client *gocloak) GetIssuer(realm string) (*IssuerResponse, error) {
 
 // RetrospectToken calls the openid-connect introspect endpoint
 func (client *gocloak) RetrospectToken(accessToken string, clientID, clientSecret string, realm string) (*RetrospecTokenResult, error) {
+	const errMessage = "could not introspect requesting party token"
+
 	var result RetrospecTokenResult
 	resp, err := client.getRequestWithBasicAuth(clientID, clientSecret).
 		SetFormData(map[string]string{
@@ -224,7 +251,7 @@ func (client *gocloak) RetrospectToken(accessToken string, clientID, clientSecre
 		SetResult(&result).
 		Post(client.getRealmURL(realm, tokenEndpoint, "introspect"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -233,19 +260,21 @@ func (client *gocloak) RetrospectToken(accessToken string, clientID, clientSecre
 
 // DecodeAccessToken decodes the accessToken
 func (client *gocloak) DecodeAccessToken(accessToken, realm string) (*jwt.Token, *jwt.MapClaims, error) {
+	const errMessage = "could not decode access token"
+
 	decodedHeader, err := jwx.DecodeAccessTokenHeader(accessToken)
 	if err != nil {
-		return nil, nil, err
+		return nil, nil, errors.Wrap(err, errMessage)
 	}
 
 	certResult, err := client.GetCerts(realm)
 	if err != nil {
-		return nil, nil, err
+		return nil, nil, errors.Wrap(err, errMessage)
 	}
 
 	usedKey := findUsedKey(decodedHeader.Kid, certResult.Keys)
 	if usedKey == nil {
-		return nil, nil, errors.New("cannot find a key to decode the token")
+		return nil, nil, errors.Wrap(errors.New("cannot find a key to decode the token"), errMessage)
 	}
 
 	return jwx.DecodeAccessToken(accessToken, usedKey.E, usedKey.N)
@@ -253,37 +282,43 @@ func (client *gocloak) DecodeAccessToken(accessToken, realm string) (*jwt.Token,
 
 // DecodeAccessTokenCustomClaims decodes the accessToken and writes claims into the given claims
 func (client *gocloak) DecodeAccessTokenCustomClaims(accessToken string, realm string, claims jwt.Claims) (*jwt.Token, error) {
+	const errMessage = "could not decode access token with custom claims"
+
 	decodedHeader, err := jwx.DecodeAccessTokenHeader(accessToken)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	certResult, err := client.GetCerts(realm)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	usedKey := findUsedKey(decodedHeader.Kid, certResult.Keys)
 	if usedKey == nil {
-		return nil, errors.New("cannot find a key to decode the token")
+		return nil, errors.Wrap(errors.New("cannot find a key to decode the token"), errMessage)
 	}
 
 	return jwx.DecodeAccessTokenCustomClaims(accessToken, usedKey.E, usedKey.N, claims)
 }
 
 func (client *gocloak) GetToken(realm string, options TokenOptions) (*JWT, error) {
+	const errMessage = "could not get token"
+
 	var token JWT
 	var req *resty.Request
+
 	if !NilOrEmpty(options.ClientSecret) {
 		req = client.getRequestWithBasicAuth(*(options.ClientID), *(options.ClientSecret))
 	} else {
 		req = client.getRequest()
 	}
+
 	resp, err := req.SetFormData(options.FormData()).
 		SetResult(&token).
 		Post(client.getRealmURL(realm, tokenEndpoint))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -292,6 +327,8 @@ func (client *gocloak) GetToken(realm string, options TokenOptions) (*JWT, error
 
 // GetRequestingPartyToken returns a requesting party token with permissions granted by the server
 func (client *gocloak) GetRequestingPartyToken(token, realm string, options RequestingPartyTokenOptions) (*JWT, error) {
+	const errMessage = "could not get requesting party token"
+
 	var res JWT
 
 	resp, err := client.getRequestWithBearerAuth(token).
@@ -300,7 +337,7 @@ func (client *gocloak) GetRequestingPartyToken(token, realm string, options Requ
 		SetResult(&res).
 		Post(client.getRealmURL(realm, tokenEndpoint))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -349,6 +386,8 @@ func (client *gocloak) Login(clientID, clientSecret, realm, username, password s
 
 // Logout logs out users with refresh token
 func (client *gocloak) Logout(clientID, clientSecret, realm, refreshToken string) error {
+	const errMessage = "could not logout"
+
 	resp, err := client.getRequestWithBasicAuth(clientID, clientSecret).
 		SetFormData(map[string]string{
 			"client_id":     clientID,
@@ -356,10 +395,12 @@ func (client *gocloak) Logout(clientID, clientSecret, realm, refreshToken string
 		}).
 		Post(client.getRealmURL(realm, logoutEndpoint))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) LogoutPublicClient(clientID, realm, accessToken, refreshToken string) error {
+	const errMessage = "could not logout public client"
+
 	resp, err := client.getRequestWithBearerAuth(accessToken).
 		SetFormData(map[string]string{
 			"client_id":     clientID,
@@ -367,29 +408,34 @@ func (client *gocloak) LogoutPublicClient(clientID, realm, accessToken, refreshT
 		}).
 		Post(client.getRealmURL(realm, logoutEndpoint))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // ExecuteActionsEmail executes an actions email
 func (client *gocloak) ExecuteActionsEmail(token, realm string, params ExecuteActionsEmail) error {
+	const errMessage = "could not execute actions email"
+
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
-		return err
+		return errors.Wrap(err, errMessage)
 	}
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(params.Actions).
 		SetQueryParams(queryParams).
 		Put(client.getAdminRealmURL(realm, "users", *(params.UserID), "execute-actions-email"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) CreateGroup(token, realm string, group Group) (string, error) {
+	const errMessage = "could not create group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(group).
 		Post(client.getAdminRealmURL(realm, "groups"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
 	return getID(resp), nil
@@ -397,147 +443,183 @@ func (client *gocloak) CreateGroup(token, realm string, group Group) (string, er
 
 // CreateChildGroup creates a new child group
 func (client *gocloak) CreateChildGroup(token string, realm string, groupID string, group Group) (string, error) {
+	const errMessage = "could not create child group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(group).
 		Post(client.getAdminRealmURL(realm, "groups", groupID, "children"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 func (client *gocloak) CreateComponent(token, realm string, component Component) (string, error) {
+	const errMessage = "could not create component"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(component).
 		Post(client.getAdminRealmURL(realm, "components"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 func (client *gocloak) CreateClient(token, realm string, newClient Client) (string, error) {
+	const errMessage = "could not create client"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(newClient).
 		Post(client.getAdminRealmURL(realm, "clients"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 // CreateClientRole creates a new role for a client
 func (client *gocloak) CreateClientRole(token, realm, clientID string, role Role) (string, error) {
+	const errMessage = "could not create client role"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(role).
 		Post(client.getAdminRealmURL(realm, "clients", clientID, "roles"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 // CreateClientScope creates a new client scope
 func (client *gocloak) CreateClientScope(token, realm string, scope ClientScope) (string, error) {
+	const errMessage = "could not create client scope"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(scope).
 		Post(client.getAdminRealmURL(realm, "client-scopes"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 func (client *gocloak) UpdateGroup(token, realm string, updatedGroup Group) error {
+	const errMessage = "could not update group"
+
 	if NilOrEmpty(updatedGroup.ID) {
-		return errors.New("ID of a group required")
+		return errors.Wrap(errors.New("ID of a group required"), errMessage)
 	}
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(updatedGroup).
 		Put(client.getAdminRealmURL(realm, "groups", PString(updatedGroup.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // UpdateClient updates the given Client
 func (client *gocloak) UpdateClient(token, realm string, updatedClient Client) error {
+	const errMessage = "could not update client"
+
 	if NilOrEmpty(updatedClient.ID) {
-		return errors.New("ID of a client required")
+		return errors.Wrap(errors.New("ID of a client required"), errMessage)
 	}
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(updatedClient).
 		Put(client.getAdminRealmURL(realm, "clients", PString(updatedClient.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) UpdateRole(token, realm, clientID string, role Role) error {
+	const errMessage = "could not update role"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(role).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "roles", PString(role.Name)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) UpdateClientScope(token string, realm string, scope ClientScope) error {
+	const errMessage = "could not update client scope"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(scope).
 		Put(client.getAdminRealmURL(realm, "client-scopes", PString(scope.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) DeleteGroup(token string, realm string, groupID string) error {
+	const errMessage = "could not delete group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "groups", groupID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteClient deletes a given client
 func (client *gocloak) DeleteClient(token string, realm string, clientID string) error {
+	const errMessage = "could not delete client"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) DeleteComponent(token string, realm string, componentID string) error {
+	const errMessage = "could not delete component"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "components", componentID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteClientRole deletes a given role
 func (client *gocloak) DeleteClientRole(token, realm, clientID, roleName string) error {
+	const errMessage = "could not delete client role"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "roles", roleName))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) DeleteClientScope(token string, realm string, scopeID string) error {
+	const errMessage = "could not delete client scope"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "client-scopes", scopeID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetClient returns a client
 func (client *gocloak) GetClient(token string, realm string, clientID string) (*Client, error) {
+	const errMessage = "could not get client"
+
 	var result Client
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -546,13 +628,15 @@ func (client *gocloak) GetClient(token string, realm string, clientID string) (*
 
 // GetClientsDefaultScopes returns a list of the client's default scopes
 func (client *gocloak) GetClientsDefaultScopes(token string, realm string, clientID string) ([]*ClientScope, error) {
+	const errMessage = "could not get clients default scopes"
+
 	var result []*ClientScope
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "default-client-scopes"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -561,29 +645,35 @@ func (client *gocloak) GetClientsDefaultScopes(token string, realm string, clien
 
 // AddDefaultScopeToClient adds a client scope to the list of client's default scopes
 func (client *gocloak) AddDefaultScopeToClient(token string, realm string, clientID string, scopeID string) error {
+	const errMessage = "could not add default scope to client"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "default-client-scopes", scopeID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // RemoveDefaultScopeFromClient removes a client scope from the list of client's default scopes
 func (client *gocloak) RemoveDefaultScopeFromClient(token string, realm string, clientID string, scopeID string) error {
+	const errMessage = "could not remove default scope from client"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "default-client-scopes", scopeID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetClientsOptionalScopes returns a list of the client's optional scopes
 func (client *gocloak) GetClientsOptionalScopes(token string, realm string, clientID string) ([]*ClientScope, error) {
+	const errMessage = "could not get clients optional scopes"
+
 	var result []*ClientScope
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "optional-client-scopes"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -592,29 +682,35 @@ func (client *gocloak) GetClientsOptionalScopes(token string, realm string, clie
 
 // AddOptionalScopeToClient adds a client scope to the list of client's optional scopes
 func (client *gocloak) AddOptionalScopeToClient(token string, realm string, clientID string, scopeID string) error {
+	const errMessage = "could not add optional scope to client"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "optional-client-scopes", scopeID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // RemoveOptionalScopeFromClient deletes a client scope from the list of client's optional scopes
 func (client *gocloak) RemoveOptionalScopeFromClient(token string, realm string, clientID string, scopeID string) error {
+	const errMessage = "could not remove optional scope from client"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "optional-client-scopes", scopeID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetDefaultOptionalClientScopes returns a list of default realm optional scopes
 func (client *gocloak) GetDefaultOptionalClientScopes(token string, realm string) ([]*ClientScope, error) {
+	const errMessage = "could not get default optional client scopes"
+
 	var result []*ClientScope
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "default-optional-client-scopes"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -623,13 +719,15 @@ func (client *gocloak) GetDefaultOptionalClientScopes(token string, realm string
 
 // GetDefaultDefaultClientScopes returns a list of default realm default scopes
 func (client *gocloak) GetDefaultDefaultClientScopes(token string, realm string) ([]*ClientScope, error) {
+	const errMessage = "could not get default client scopes"
+
 	var result []*ClientScope
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "default-default-client-scopes"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -638,13 +736,15 @@ func (client *gocloak) GetDefaultDefaultClientScopes(token string, realm string)
 
 // GetClientScope returns a clientscope
 func (client *gocloak) GetClientScope(token string, realm string, scopeID string) (*ClientScope, error) {
+	const errMessage = "could not get client scope"
+
 	var result ClientScope
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "client-scopes", scopeID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -653,13 +753,15 @@ func (client *gocloak) GetClientScope(token string, realm string, scopeID string
 
 // GetClientScopes returns all client scopes
 func (client *gocloak) GetClientScopes(token string, realm string) ([]*ClientScope, error) {
+	const errMessage = "could not get client scopes"
+
 	var result []*ClientScope
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "client-scopes"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -668,12 +770,15 @@ func (client *gocloak) GetClientScopes(token string, realm string) ([]*ClientSco
 
 // GetClientSecret returns a client's secret
 func (client *gocloak) GetClientSecret(token string, realm string, clientID string) (*CredentialRepresentation, error) {
+	const errMessage = "could not get client secret"
+
 	var result CredentialRepresentation
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "client-secret"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -682,12 +787,14 @@ func (client *gocloak) GetClientSecret(token string, realm string, clientID stri
 
 // GetClientServiceAccount retrieves the service account "user" for a client if enabled
 func (client *gocloak) GetClientServiceAccount(token string, realm string, clientID string) (*User, error) {
+	const errMessage = "could not get client service account"
+
 	var result User
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "service-account-user"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -695,80 +802,98 @@ func (client *gocloak) GetClientServiceAccount(token string, realm string, clien
 }
 
 func (client *gocloak) RegenerateClientSecret(token string, realm string, clientID string) (*CredentialRepresentation, error) {
+	const errMessage = "could not regenerate client secret"
+
 	var result CredentialRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Post(client.getAdminRealmURL(realm, "clients", clientID, "client-secret"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
+
 	return &result, nil
 }
 
 // GetClientOfflineSessions returns offline sessions associated with the client
 func (client *gocloak) GetClientOfflineSessions(token, realm, clientID string) ([]*UserSessionRepresentation, error) {
+	const errMessage = "could not get client offline sessions"
+
 	var res []*UserSessionRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&res).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "offline-sessions"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
+
 	return res, nil
 }
 
 // GetClientUserSessions returns user sessions associated with the client
 func (client *gocloak) GetClientUserSessions(token, realm, clientID string) ([]*UserSessionRepresentation, error) {
+	const errMessage = "could not get client user sessions"
+
 	var res []*UserSessionRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&res).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "user-sessions"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
+
 	return res, nil
 }
 
 // CreateClientProtocolMapper creates a protocol mapper in client scope
 func (client *gocloak) CreateClientProtocolMapper(token, realm, clientID string, mapper ProtocolMapperRepresentation) (string, error) {
+	const errMessage = "could not create client protocol mapper"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(mapper).
 		Post(client.getAdminRealmURL(realm, "clients", clientID, "protocol-mappers", "models"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 // UpdateClientProtocolMapper updates a protocol mapper in client scope
 func (client *gocloak) UpdateClientProtocolMapper(token, realm, clientID string, mapperID string, mapper ProtocolMapperRepresentation) error {
+	const errMessage = "could not update client protocol mapper"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(mapper).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "protocol-mappers", "models", mapperID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteClientProtocolMapper deletes a protocol mapper in client scope
 func (client *gocloak) DeleteClientProtocolMapper(token, realm, clientID, mapperID string) error {
+	const errMessage = "could not delete client protocol mapper"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "protocol-mappers", "models", mapperID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetKeyStoreConfig get keystoreconfig of the realm
 func (client *gocloak) GetKeyStoreConfig(token string, realm string) (*KeyStoreConfig, error) {
+	const errMessage = "could not get key store config"
+
 	var result KeyStoreConfig
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "keys"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -777,12 +902,14 @@ func (client *gocloak) GetKeyStoreConfig(token string, realm string) (*KeyStoreC
 
 // GetComponents get all components in realm
 func (client *gocloak) GetComponents(token string, realm string) ([]*Component, error) {
+	const errMessage = "could not get components"
+
 	var result []*Component
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "components"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -791,13 +918,15 @@ func (client *gocloak) GetComponents(token string, realm string) ([]*Component,
 
 // GetDefaultGroups returns a list of default groups
 func (client *gocloak) GetDefaultGroups(token string, realm string) ([]*Group, error) {
+	const errMessage = "could not get default groups"
+
 	var result []*Group
 
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "default-groups"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -806,27 +935,33 @@ func (client *gocloak) GetDefaultGroups(token string, realm string) ([]*Group, e
 
 // AddDefaultGroup adds group to the list of default groups
 func (client *gocloak) AddDefaultGroup(token string, realm string, groupID string) error {
+	const errMessage = "could not add default group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Put(client.getAdminRealmURL(realm, "default-groups", groupID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // RemoveDefaultGroup removes group from the list of default groups
 func (client *gocloak) RemoveDefaultGroup(token string, realm string, groupID string) error {
+	const errMessage = "could not remove default group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "default-groups", groupID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) getRoleMappings(token string, realm string, path string, objectID string) (*MappingsRepresentation, error) {
+	const errMessage = "could not get role mappings"
+
 	var result MappingsRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, path, objectID, "role-mappings"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -845,12 +980,15 @@ func (client *gocloak) GetRoleMappingByUserID(token string, realm string, userID
 
 // GetGroup get group with id in realm
 func (client *gocloak) GetGroup(token string, realm string, groupID string) (*Group, error) {
+	const errMessage = "could not get group"
+
 	var result Group
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "groups", groupID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -859,10 +997,12 @@ func (client *gocloak) GetGroup(token string, realm string, groupID string) (*Gr
 
 // GetGroups get all groups in realm
 func (client *gocloak) GetGroups(token string, realm string, params GetGroupsParams) ([]*Group, error) {
+	const errMessage = "could not get groups"
+
 	var result []*Group
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	resp, err := client.getRequestWithBearerAuth(token).
@@ -870,7 +1010,7 @@ func (client *gocloak) GetGroups(token string, realm string, params GetGroupsPar
 		SetQueryParams(queryParams).
 		Get(client.getAdminRealmURL(realm, "groups"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -879,10 +1019,12 @@ func (client *gocloak) GetGroups(token string, realm string, params GetGroupsPar
 
 // GetGroupMembers get a list of users of group with id in realm
 func (client *gocloak) GetGroupMembers(token string, realm string, groupID string, params GetGroupsParams) ([]*User, error) {
+	const errMessage = "could not get group members"
+
 	var result []*User
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	resp, err := client.getRequestWithBearerAuth(token).
@@ -890,7 +1032,7 @@ func (client *gocloak) GetGroupMembers(token string, realm string, groupID strin
 		SetQueryParams(queryParams).
 		Get(client.getAdminRealmURL(realm, "groups", groupID, "members"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -899,12 +1041,14 @@ func (client *gocloak) GetGroupMembers(token string, realm string, groupID strin
 
 // GetClientRoles get all roles for the given client in realm
 func (client *gocloak) GetClientRoles(token string, realm string, clientID string) ([]*Role, error) {
+	const errMessage = "could not get client roles"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "roles"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -913,12 +1057,14 @@ func (client *gocloak) GetClientRoles(token string, realm string, clientID strin
 
 // GetRealmRolesByUserID returns all client roles assigned to the given user
 func (client *gocloak) GetClientRolesByUserID(token string, realm string, clientID string, userID string) ([]*Role, error) {
+	const errMessage = "could not client roles by user id"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "clients", clientID))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -927,12 +1073,14 @@ func (client *gocloak) GetClientRolesByUserID(token string, realm string, client
 
 // GetClientRolesByGroupID returns all client roles assigned to the given group
 func (client *gocloak) GetClientRolesByGroupID(token string, realm string, clientID string, groupID string) ([]*Role, error) {
+	const errMessage = "could not get client roles by group id"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "clients", clientID))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -941,12 +1089,14 @@ func (client *gocloak) GetClientRolesByGroupID(token string, realm string, clien
 
 // GetCompositeClientRolesByRoleID returns all client composite roles associated with the given client role
 func (client *gocloak) GetCompositeClientRolesByRoleID(token string, realm string, clientID string, roleID string) ([]*Role, error) {
+	const errMessage = "could not get composite client roles by role id"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "roles-by-id", roleID, "composites", "clients", clientID))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -955,12 +1105,14 @@ func (client *gocloak) GetCompositeClientRolesByRoleID(token string, realm strin
 
 // GetCompositeClientRolesByUserID returns all client roles and composite roles assigned to the given user
 func (client *gocloak) GetCompositeClientRolesByUserID(token string, realm string, clientID string, userID string) ([]*Role, error) {
+	const errMessage = "could not get composite client roles by user id"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "clients", clientID, "composite"))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -969,12 +1121,14 @@ func (client *gocloak) GetCompositeClientRolesByUserID(token string, realm strin
 
 // GetCompositeClientRolesByGroupID returns all client roles and composite roles assigned to the given group
 func (client *gocloak) GetCompositeClientRolesByGroupID(token string, realm string, clientID string, groupID string) ([]*Role, error) {
+	const errMessage = "could not get composite client roles by group id"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "clients", clientID, "composite"))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -983,12 +1137,14 @@ func (client *gocloak) GetCompositeClientRolesByGroupID(token string, realm stri
 
 // GetClientRole get a role for the given client in a realm by role name
 func (client *gocloak) GetClientRole(token string, realm string, clientID string, roleName string) (*Role, error) {
+	const errMessage = "could not get client role"
+
 	var result Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "roles", roleName))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -997,17 +1153,19 @@ func (client *gocloak) GetClientRole(token string, realm string, clientID string
 
 // GetClients gets all clients in realm
 func (client *gocloak) GetClients(token string, realm string, params GetClientsParams) ([]*Client, error) {
+	const errMessage = "could not get clients"
+
 	var result []*Client
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		SetQueryParams(queryParams).
 		Get(client.getAdminRealmURL(realm, "clients"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1032,24 +1190,30 @@ func (client *gocloak) UserAttributeContains(attributes map[string][]string, att
 
 // CreateRealmRole creates a role in a realm
 func (client *gocloak) CreateRealmRole(token string, realm string, role Role) (string, error) {
+	const errMessage = "could not create realm role"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(role).
 		Post(client.getAdminRealmURL(realm, "roles"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 // GetRealmRole returns a role from a realm by role's name
 func (client *gocloak) GetRealmRole(token string, realm string, roleName string) (*Role, error) {
+	const errMessage = "could not get realm role"
+
 	var result Role
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "roles", roleName))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1058,12 +1222,14 @@ func (client *gocloak) GetRealmRole(token string, realm string, roleName string)
 
 // GetRealmRoles get all roles of the given realm.
 func (client *gocloak) GetRealmRoles(token string, realm string) ([]*Role, error) {
+	const errMessage = "could not get realm roles"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "roles"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1072,12 +1238,14 @@ func (client *gocloak) GetRealmRoles(token string, realm string) ([]*Role, error
 
 // GetRealmRolesByUserID returns all roles assigned to the given user
 func (client *gocloak) GetRealmRolesByUserID(token string, realm string, userID string) ([]*Role, error) {
+	const errMessage = "could not get realm roles by user id"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "realm"))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1086,11 +1254,13 @@ func (client *gocloak) GetRealmRolesByUserID(token string, realm string, userID
 
 // GetRealmRolesByGroupID returns all roles assigned to the given group
 func (client *gocloak) GetRealmRolesByGroupID(token string, realm string, groupID string) ([]*Role, error) {
+	const errMessage = "could not get realm roles by group id"
+
 	var result []*Role
 	resp, err := client.getRequestWithBearerAuth(token).
 		Get(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "realm"))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1099,71 +1269,87 @@ func (client *gocloak) GetRealmRolesByGroupID(token string, realm string, groupI
 
 // UpdateRealmRole updates a role in a realm
 func (client *gocloak) UpdateRealmRole(token string, realm string, roleName string, role Role) error {
+	const errMessage = "could not update realm role"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(role).
 		Put(client.getAdminRealmURL(realm, "roles", roleName))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteRealmRole deletes a role in a realm by role's name
 func (client *gocloak) DeleteRealmRole(token string, realm string, roleName string) error {
+	const errMessage = "could not delete realm role"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "roles", roleName))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // AddRealmRoleToUser adds realm-level role mappings
 func (client *gocloak) AddRealmRoleToUser(token string, realm string, userID string, roles []Role) error {
+	const errMessage = "could not add realm role to user"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Post(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "realm"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteRealmRoleFromUser deletes realm-level role mappings
 func (client *gocloak) DeleteRealmRoleFromUser(token string, realm string, userID string, roles []Role) error {
+	const errMessage = "could not delete realm role from user"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Delete(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "realm"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // AddRealmRoleToGroup adds realm-level role mappings
 func (client *gocloak) AddRealmRoleToGroup(token string, realm string, groupID string, roles []Role) error {
+	const errMessage = "could not add realm role to group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Post(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "realm"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteRealmRoleFromGroup deletes realm-level role mappings
 func (client *gocloak) DeleteRealmRoleFromGroup(token string, realm string, groupID string, roles []Role) error {
+	const errMessage = "could not delete realm role from group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Delete(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "realm"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) AddRealmRoleComposite(token string, realm string, roleName string, roles []Role) error {
+	const errMessage = "could not add realm role composite"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Post(client.getAdminRealmURL(realm, "roles", roleName, "composites"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 func (client *gocloak) DeleteRealmRoleComposite(token string, realm string, roleName string, roles []Role) error {
+	const errMessage = "could not delete realm role composite"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Delete(client.getAdminRealmURL(realm, "roles", roleName, "composites"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // -----
@@ -1172,12 +1358,14 @@ func (client *gocloak) DeleteRealmRoleComposite(token string, realm string, role
 
 // GetRealm returns top-level representation of the realm
 func (client *gocloak) GetRealm(token string, realm string) (*RealmRepresentation, error) {
+	const errMessage = "could not get realm"
+
 	var result RealmRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1186,12 +1374,14 @@ func (client *gocloak) GetRealm(token string, realm string) (*RealmRepresentatio
 
 // GetRealms returns top-level representation of all realms
 func (client *gocloak) GetRealms(token string) ([]*RealmRepresentation, error) {
+	const errMessage = "could not get realms"
+
 	var result []*RealmRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(""))
 
-	if err = checkForError(resp, err); err != nil {
+	if err = checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1200,11 +1390,13 @@ func (client *gocloak) GetRealms(token string) ([]*RealmRepresentation, error) {
 
 // CreateRealm creates a realm
 func (client *gocloak) CreateRealm(token string, realm RealmRepresentation) (string, error) {
+	const errMessage = "could not create realm"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(&realm).
 		Post(client.getAdminRealmURL(""))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
 	return getID(resp), nil
@@ -1212,39 +1404,53 @@ func (client *gocloak) CreateRealm(token string, realm RealmRepresentation) (str
 
 // UpdateRealm updates a given realm
 func (client *gocloak) UpdateRealm(token string, realm RealmRepresentation) error {
+	const errMessage = "could not update realm"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(realm).
 		Put(client.getAdminRealmURL(PString(realm.Realm)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteRealm removes a realm
 func (client *gocloak) DeleteRealm(token string, realm string) error {
+	const errMessage = "could not delete realm"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm))
-	return checkForError(resp, err)
+
+	return checkForError(resp, err, errMessage)
 }
 
 // ClearRealmCache clears realm cache
 func (client *gocloak) ClearRealmCache(token string, realm string) error {
+	const errMessage = "could not clear realm cache"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Post(client.getAdminRealmURL(realm, "clear-realm-cache"))
-	return checkForError(resp, err)
+
+	return checkForError(resp, err, errMessage)
 }
 
 // ClearUserCache clears realm cache
 func (client *gocloak) ClearUserCache(token string, realm string) error {
+	const errMessage = "could not clear user cache"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Post(client.getAdminRealmURL(realm, "clear-user-cache"))
-	return checkForError(resp, err)
+
+	return checkForError(resp, err, errMessage)
 }
 
 // ClearKeysCache clears realm cache
 func (client *gocloak) ClearKeysCache(token string, realm string) error {
+	const errMessage = "could not clear keys cache"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Post(client.getAdminRealmURL(realm, "clear-keys-cache"))
-	return checkForError(resp, err)
+
+	return checkForError(resp, err, errMessage)
 }
 
 // -----
@@ -1253,28 +1459,35 @@ func (client *gocloak) ClearKeysCache(token string, realm string) error {
 
 // CreateUser creates the given user in the given realm and returns it's userID
 func (client *gocloak) CreateUser(token string, realm string, user User) (string, error) {
+	const errMessage = "could not create user"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(user).
 		Post(client.getAdminRealmURL(realm, "users"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
+
 	return getID(resp), nil
 }
 
 // DeleteUser delete a given user
 func (client *gocloak) DeleteUser(token string, realm string, userID string) error {
+	const errMessage = "could not delete user"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "users", userID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetUserByID fetches a user from the given realm with the given userID
 func (client *gocloak) GetUserByID(accessToken string, realm string, userID string) (*User, error) {
+	const errMessage = "could not get user by id"
+
 	if userID == "" {
-		return nil, errors.New("userID shall not be empty")
+		return nil, errors.Wrap(errors.New("userID shall not be empty"), errMessage)
 	}
 
 	var result User
@@ -1282,7 +1495,7 @@ func (client *gocloak) GetUserByID(accessToken string, realm string, userID stri
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "users", userID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1291,13 +1504,15 @@ func (client *gocloak) GetUserByID(accessToken string, realm string, userID stri
 
 // GetUserCount gets the user count in the realm
 func (client *gocloak) GetUserCount(token string, realm string) (int, error) {
+	const errMessage = "could not get user count"
+
 	var result int
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "users", "count"))
 
-	if err := checkForError(resp, err); err != nil {
-		return -1, err
+	if err := checkForError(resp, err, errMessage); err != nil {
+		return -1, errors.Wrap(err, errMessage)
 	}
 
 	return result, nil
@@ -1305,12 +1520,14 @@ func (client *gocloak) GetUserCount(token string, realm string) (int, error) {
 
 // GetUserGroups get all groups for user
 func (client *gocloak) GetUserGroups(token string, realm string, userID string) ([]*UserGroup, error) {
+	const errMessage = "could not get user groups"
+
 	var result []*UserGroup
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "users", userID, "groups"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1319,10 +1536,12 @@ func (client *gocloak) GetUserGroups(token string, realm string, userID string)
 
 // GetUsers get all users in realm
 func (client *gocloak) GetUsers(token string, realm string, params GetUsersParams) ([]*User, error) {
+	const errMessage = "could not get users"
+
 	var result []*User
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	resp, err := client.getRequestWithBearerAuth(token).
@@ -1330,7 +1549,7 @@ func (client *gocloak) GetUsers(token string, realm string, params GetUsersParam
 		SetQueryParams(queryParams).
 		Get(client.getAdminRealmURL(realm, "users"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1339,12 +1558,14 @@ func (client *gocloak) GetUsers(token string, realm string, params GetUsersParam
 
 // GetUsersByRoleName returns all users have a given role
 func (client *gocloak) GetUsersByRoleName(token string, realm string, roleName string) ([]*User, error) {
+	const errMessage = "could not get users by role name"
+
 	var result []*User
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "roles", roleName, "users"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1353,6 +1574,8 @@ func (client *gocloak) GetUsersByRoleName(token string, realm string, roleName s
 
 // GetUsersByClientRoleName returns all users have a given client role
 func (client *gocloak) GetUsersByClientRoleName(token string, realm string, clientID string, roleName string, params GetUsersByRoleParams) ([]*User, error) {
+	const errMessage = "could not get users by client role name"
+
 	var result []*User
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
@@ -1364,7 +1587,7 @@ func (client *gocloak) GetUsersByClientRoleName(token string, realm string, clie
 		SetQueryParams(queryParams).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "roles", roleName, "users"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1373,117 +1596,143 @@ func (client *gocloak) GetUsersByClientRoleName(token string, realm string, clie
 
 // SetPassword sets a new password for the user with the given id. Needs elevated privileges
 func (client *gocloak) SetPassword(token string, userID string, realm string, password string, temporary bool) error {
+	const errMessage = "could not set password"
+
 	requestBody := SetPasswordRequest{Password: &password, Temporary: &temporary, Type: StringP("password")}
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(requestBody).
 		Put(client.getAdminRealmURL(realm, "users", userID, "reset-password"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // UpdateUser updates a given user
 func (client *gocloak) UpdateUser(token string, realm string, user User) error {
+	const errMessage = "could not update user"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(user).
 		Put(client.getAdminRealmURL(realm, "users", PString(user.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // AddUserToGroup puts given user to given group
 func (client *gocloak) AddUserToGroup(token string, realm string, userID string, groupID string) error {
+	const errMessage = "could not add user to group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Put(client.getAdminRealmURL(realm, "users", userID, "groups", groupID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteUserFromGroup deletes given user from given group
 func (client *gocloak) DeleteUserFromGroup(token string, realm string, userID string, groupID string) error {
+	const errMessage = "could not delete user from group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "users", userID, "groups", groupID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetUserSessions returns user sessions associated with the user
 func (client *gocloak) GetUserSessions(token, realm, userID string) ([]*UserSessionRepresentation, error) {
+	const errMessage = "could not get user sessions"
+
 	var res []*UserSessionRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&res).
 		Get(client.getAdminRealmURL(realm, "users", userID, "sessions"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
+
 	return res, nil
 }
 
 // GetUserOfflineSessionsForClient returns offline sessions associated with the user and client
 func (client *gocloak) GetUserOfflineSessionsForClient(token, realm, userID, clientID string) ([]*UserSessionRepresentation, error) {
+	const errMessage = "could not get user offline sessions for client"
+
 	var res []*UserSessionRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&res).
 		Get(client.getAdminRealmURL(realm, "users", userID, "offline-sessions", clientID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
+
 	return res, nil
 }
 
 // AddClientRoleToUser adds client-level role mappings
 func (client *gocloak) AddClientRoleToUser(token string, realm string, clientID string, userID string, roles []Role) error {
+	const errMessage = "could not add client role to user"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Post(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "clients", clientID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // AddClientRoleToGroup adds a client role to the group
 func (client *gocloak) AddClientRoleToGroup(token string, realm string, clientID string, groupID string, roles []Role) error {
+	const errMessage = "could not add client role to group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Post(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "clients", clientID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteClientRoleFromUser adds client-level role mappings
 func (client *gocloak) DeleteClientRoleFromUser(token string, realm string, clientID string, userID string, roles []Role) error {
+	const errMessage = "could not delete client role from user"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Delete(client.getAdminRealmURL(realm, "users", userID, "role-mappings", "clients", clientID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteClientRoleFromGroup removes a client role from from the group
 func (client *gocloak) DeleteClientRoleFromGroup(token string, realm string, clientID string, groupID string, roles []Role) error {
+	const errMessage = "could not client role from group"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Delete(client.getAdminRealmURL(realm, "groups", groupID, "role-mappings", "clients", clientID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // AddClientRoleComposite adds roles as composite
 func (client *gocloak) AddClientRoleComposite(token string, realm string, roleID string, roles []Role) error {
+	const errMessage = "could not add client role composite"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Post(client.getAdminRealmURL(realm, "roles-by-id", roleID, "composites"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteClientRoleComposite deletes composites from a role
 func (client *gocloak) DeleteClientRoleComposite(token string, realm string, roleID string, roles []Role) error {
+	const errMessage = "could not delete client role composite"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(roles).
 		Delete(client.getAdminRealmURL(realm, "roles-by-id", roleID, "composites"))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // ------------------
@@ -1492,11 +1741,13 @@ func (client *gocloak) DeleteClientRoleComposite(token string, realm string, rol
 
 // CreateIdentityProvider creates an identity provider in a realm
 func (client *gocloak) CreateIdentityProvider(token string, realm string, providerRep IdentityProviderRepresentation) (string, error) {
+	const errMessage = "could not create identity provider"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(providerRep).
 		Post(client.getAdminRealmURL(realm, "identity-provider", "instances"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return "", err
 	}
 
@@ -1505,12 +1756,14 @@ func (client *gocloak) CreateIdentityProvider(token string, realm string, provid
 
 // GetIdentityProviders returns list of identity providers in a realm
 func (client *gocloak) GetIdentityProviders(token string, realm string) ([]*IdentityProviderRepresentation, error) {
+	const errMessage = "could not get identity providers"
+
 	result := []*IdentityProviderRepresentation{}
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "identity-provider", "instances"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1519,12 +1772,14 @@ func (client *gocloak) GetIdentityProviders(token string, realm string) ([]*Iden
 
 // GetIdentityProvider gets the identity provider in a realm
 func (client *gocloak) GetIdentityProvider(token string, realm string, alias string) (*IdentityProviderRepresentation, error) {
+	const errMessage = "could not get identity provider"
+
 	result := IdentityProviderRepresentation{}
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "identity-provider", "instances", alias))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
@@ -1533,102 +1788,125 @@ func (client *gocloak) GetIdentityProvider(token string, realm string, alias str
 
 // UpdateIdentityProvider updates the identity provider in a realm
 func (client *gocloak) UpdateIdentityProvider(token string, realm string, alias string, providerRep IdentityProviderRepresentation) error {
+	const errMessage = "could not update identity provider"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(providerRep).
 		Put(client.getAdminRealmURL(realm, "identity-provider", "instances", alias))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteIdentityProvider deletes the identity provider in a realm
 func (client *gocloak) DeleteIdentityProvider(token string, realm string, alias string) error {
+	const errMessage = "could not delete identity provider"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "identity-provider", "instances", alias))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetResource returns a client's resource with the given id
 func (client *gocloak) GetResource(token string, realm string, clientID string, resourceID string) (*ResourceRepresentation, error) {
+	const errMessage = "could not get resource"
+
 	var result ResourceRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "resource", resourceID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return &result, err
+
+	return &result, nil
 }
 
 // GetResources returns resources associated with the client
 func (client *gocloak) GetResources(token string, realm string, clientID string, params GetResourceParams) ([]*ResourceRepresentation, error) {
+	const errMessage = "could not get resources"
+
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
 		return nil, err
 	}
+
 	var result []*ResourceRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		SetQueryParams(queryParams).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "resource"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return result, err
+
+	return result, nil
 }
 
 // CreateResource creates a resource associated with the client
 func (client *gocloak) CreateResource(token, realm string, clientID string, resource ResourceRepresentation) (*ResourceRepresentation, error) {
+	const errMessage = "could not create resource"
+
 	var result ResourceRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		SetBody(resource).
 		Post(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "resource"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
 
-	return &result, err
+	return &result, nil
 }
 
 // UpdateResource updates a resource associated with the client
 func (client *gocloak) UpdateResource(token string, realm string, clientID string, resource ResourceRepresentation) error {
+	const errMessage = "could not update resource"
+
 	if NilOrEmpty(resource.ID) {
 		return errors.New("ID of a resource required")
 	}
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(resource).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "resource", *(resource.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteResource deletes a resource associated with the client
 func (client *gocloak) DeleteResource(token string, realm string, clientID string, resourceID string) error {
+	const errMessage = "could not delete resource"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "resource", resourceID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetScope returns a client's scope with the given id
 func (client *gocloak) GetScope(token string, realm string, clientID string, scopeID string) (*ScopeRepresentation, error) {
+	const errMessage = "could not get scope"
+
 	var result ScopeRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "scope", scopeID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return &result, err
+
+	return &result, nil
 }
 
 // GetScopes returns scopes associated with the client
 func (client *gocloak) GetScopes(token string, realm string, clientID string, params GetScopeParams) ([]*ScopeRepresentation, error) {
+	const errMessage = "could not get scopes"
+
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
 		return nil, err
@@ -1639,64 +1917,78 @@ func (client *gocloak) GetScopes(token string, realm string, clientID string, pa
 		SetQueryParams(queryParams).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "scope"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return result, err
+
+	return result, nil
 }
 
 // CreateScope creates a scope associated with the client
 func (client *gocloak) CreateScope(token string, realm string, clientID string, scope ScopeRepresentation) (*ScopeRepresentation, error) {
+	const errMessage = "could not create scope"
+
 	var result ScopeRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		SetBody(scope).
 		Post(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "scope"))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return &result, err
+
+	return &result, nil
 }
 
 // UpdateScope updates a scope associated with the client
 func (client *gocloak) UpdateScope(token string, realm string, clientID string, scope ScopeRepresentation) error {
+	const errMessage = "could not update scope"
+
 	if NilOrEmpty(scope.ID) {
 		return errors.New("ID of a scope required")
 	}
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(scope).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "scope", *(scope.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeleteScope deletes a scope associated with the client
 func (client *gocloak) DeleteScope(token string, realm string, clientID string, scopeID string) error {
+	const errMessage = "could not delete scope"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "scope", scopeID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetPolicy returns a client's policy with the given id
 func (client *gocloak) GetPolicy(token string, realm string, clientID string, policyID string) (*PolicyRepresentation, error) {
+	const errMessage = "could not get policy"
+
 	var result PolicyRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "policy", policyID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return &result, err
+
+	return &result, nil
 }
 
 // GetPolicies returns policies associated with the client
 func (client *gocloak) GetPolicies(token string, realm string, clientID string, params GetPolicyParams) ([]*PolicyRepresentation, error) {
+	const errMessage = "could not get policies"
+
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	var adminURL string
@@ -1712,67 +2004,82 @@ func (client *gocloak) GetPolicies(token string, realm string, clientID string,
 		SetQueryParams(queryParams).
 		Get(adminURL)
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return result, err
+
+	return result, nil
 }
 
 // CreatePolicy creates a policy associated with the client
 func (client *gocloak) CreatePolicy(token string, realm string, clientID string, policy PolicyRepresentation) (*PolicyRepresentation, error) {
+	const errMessage = "could not create policy"
+
 	if NilOrEmpty(policy.Type) {
 		return nil, errors.New("type of a policy required")
 	}
+
 	var result PolicyRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		SetBody(policy).
 		Post(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "policy", *(policy.Type)))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return &result, err
+
+	return &result, nil
 }
 
 // UpdatePolicy updates a policy associated with the client
 func (client *gocloak) UpdatePolicy(token string, realm string, clientID string, policy PolicyRepresentation) error {
+	const errMessage = "could not update policy"
+
 	if NilOrEmpty(policy.ID) {
 		return errors.New("ID of a policy required")
 	}
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetBody(policy).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "policy", *(policy.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeletePolicy deletes a policy associated with the client
 func (client *gocloak) DeletePolicy(token string, realm string, clientID string, policyID string) error {
+	const errMessage = "could not delete policy"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "policy", policyID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // GetPermission returns a client's permission with the given id
 func (client *gocloak) GetPermission(token string, realm string, clientID string, permissionID string) (*PermissionRepresentation, error) {
+	const errMessage = "could not get permission"
+
 	var result PermissionRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		Get(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "permission", permissionID))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return &result, err
+
+	return &result, nil
 }
 
 // GetPolicies returns permissions associated with the client
 func (client *gocloak) GetPermissions(token string, realm string, clientID string, params GetPermissionParams) ([]*PermissionRepresentation, error) {
+	const errMessage = "could not get permissions"
+
 	queryParams, err := GetQueryParams(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	var adminURL string
@@ -1788,31 +2095,38 @@ func (client *gocloak) GetPermissions(token string, realm string, clientID strin
 		SetQueryParams(queryParams).
 		Get(adminURL)
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return result, err
+
+	return result, nil
 }
 
 // CreatePermission creates a permission associated with the client
 func (client *gocloak) CreatePermission(token string, realm string, clientID string, permission PermissionRepresentation) (*PermissionRepresentation, error) {
+	const errMessage = "could not craete permission"
+
 	if NilOrEmpty(permission.Type) {
 		return nil, errors.New("type of a permission required")
 	}
+
 	var result PermissionRepresentation
 	resp, err := client.getRequestWithBearerAuth(token).
 		SetResult(&result).
 		SetBody(permission).
 		Post(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "permission", *(permission.Type)))
 
-	if err := checkForError(resp, err); err != nil {
+	if err := checkForError(resp, err, errMessage); err != nil {
 		return nil, err
 	}
-	return &result, err
+
+	return &result, nil
 }
 
 // UpdatePermission updates a permission associated with the client
 func (client *gocloak) UpdatePermission(token string, realm string, clientID string, permission PermissionRepresentation) error {
+	const errMessage = "could not update permission"
+
 	if NilOrEmpty(permission.ID) {
 		return errors.New("ID of a permission required")
 	}
@@ -1820,13 +2134,15 @@ func (client *gocloak) UpdatePermission(token string, realm string, clientID str
 		SetBody(permission).
 		Put(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "permission", *(permission.ID)))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
 
 // DeletePermission deletes a policy associated with the client
 func (client *gocloak) DeletePermission(token string, realm string, clientID string, permissionID string) error {
+	const errMessage = "could not delete permission"
+
 	resp, err := client.getRequestWithBearerAuth(token).
 		Delete(client.getAdminRealmURL(realm, "clients", clientID, "authz", "resource-server", "permission", permissionID))
 
-	return checkForError(resp, err)
+	return checkForError(resp, err, errMessage)
 }
diff --git a/errors.go b/errors.go
index b8e9c64..83dbbd2 100644
--- a/errors.go
+++ b/errors.go
@@ -1,20 +1,5 @@
 package gocloak
 
-// ObjectAlreadyExists is used when keycloak answers with 409
-type ObjectAlreadyExists struct {
-	ErrorMessage string
-}
-
-func (e *ObjectAlreadyExists) Error() string {
-	return e.ErrorMessage
-}
-
-// IsObjectAlreadyExists is a helper to verify tht the err is ObjectAlreadyExists
-func IsObjectAlreadyExists(err error) bool {
-	_, ok := err.(*ObjectAlreadyExists)
-	return ok
-}
-
 // HTTPErrorResponse is a model of an error response
 type HTTPErrorResponse struct {
 	ErrorMessage string `json:"errorMessage,omitempty"`
diff --git a/go.mod b/go.mod
index 5aef5d3..578b067 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-resty/resty/v2 v2.0.0
+	github.com/pkg/errors v0.9.1
 	github.com/stretchr/testify v1.4.0
 	gopkg.in/yaml.v2 v2.2.7 // indirect
 )
diff --git a/go.sum b/go.sum
index 937ea64..b3eea8e 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,3 @@
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -6,12 +5,11 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/go-resty/resty/v2 v2.0.0 h1:9Nq/U+V4xsoDnDa/iTrABDWUCuk3Ne92XFHPe6dKWUc=
 github.com/go-resty/resty/v2 v2.0.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -19,8 +17,8 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
 gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/models.go b/models.go
index 946fa75..4f92df5 100644
--- a/models.go
+++ b/models.go
@@ -58,10 +58,10 @@ func (s *StringOrArray) MarshalJSON() ([]byte, error) {
 	return json.Marshal([]string(*s))
 }
 
-// APIError represents an api error
+// APIError holds message and statusCode for api errors
 type APIError struct {
-	Code    int
-	Message string
+	Code    int    `json:"code"`
+	Message string `json:"message"`
 }
 
 // Error stringifies the APIError
diff --git a/pkg/jwx/jwx.go b/pkg/jwx/jwx.go
index 40ca11e..8e84b07 100644
--- a/pkg/jwx/jwx.go
+++ b/pkg/jwx/jwx.go
@@ -11,38 +11,44 @@ import (
 	"strings"
 
 	jwt "github.com/dgrijalva/jwt-go"
+	"github.com/pkg/errors"
 )
 
 // DecodeAccessTokenHeader decodes the header of the accessToken
 func DecodeAccessTokenHeader(token string) (*DecodedAccessTokenHeader, error) {
+	const errMessage = "could not decode access token header"
 	token = strings.Replace(token, "Bearer ", "", 1)
 	headerString := strings.Split(token, ".")
 	decodedData, err := base64.RawStdEncoding.DecodeString(headerString[0])
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	result := &DecodedAccessTokenHeader{}
 	err = json.Unmarshal(decodedData, result)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	return result, nil
 }
 
 func decodePublicKey(e, n *string) (*rsa.PublicKey, error) {
+	const errMessage = "could not decode public key"
+
 	decN, err := base64.RawURLEncoding.DecodeString(*n)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
+
 	nInt := big.NewInt(0)
 	nInt.SetBytes(decN)
 
 	decE, err := base64.RawURLEncoding.DecodeString(*e)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
+
 	var eBytes []byte
 	if len(decE) < 8 {
 		eBytes = make([]byte, 8-len(decE), 8)
@@ -55,17 +61,20 @@ func decodePublicKey(e, n *string) (*rsa.PublicKey, error) {
 	var eInt uint64
 	err = binary.Read(eReader, binary.BigEndian, &eInt)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
+
 	pKey := rsa.PublicKey{N: nInt, E: int(eInt)}
 	return &pKey, nil
 }
 
 // DecodeAccessToken currently only supports RSA - sorry for that
 func DecodeAccessToken(accessToken string, e, n *string) (*jwt.Token, *jwt.MapClaims, error) {
+	const errMessage = "could not decode accessToken"
+
 	rsaPublicKey, err := decodePublicKey(e, n)
 	if err != nil {
-		return nil, nil, err
+		return nil, nil, errors.Wrap(err, errMessage)
 	}
 
 	claims := &jwt.MapClaims{}
@@ -77,14 +86,20 @@ func DecodeAccessToken(accessToken string, e, n *string) (*jwt.Token, *jwt.MapCl
 		return rsaPublicKey, nil
 	})
 
-	return token2, claims, err
+	if err != nil {
+		return nil, nil, errors.Wrap(err, errMessage)
+	}
+
+	return token2, claims, nil
 }
 
 // DecodeAccessTokenCustomClaims currently only supports RSA - sorry for that
 func DecodeAccessTokenCustomClaims(accessToken string, e, n *string, customClaims jwt.Claims) (*jwt.Token, error) {
+	const errMessage = "could not decode accessToken with custom claims"
+
 	rsaPublicKey, err := decodePublicKey(e, n)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, errMessage)
 	}
 
 	token2, err := jwt.ParseWithClaims(accessToken, customClaims, func(token *jwt.Token) (interface{}, error) {
@@ -95,5 +110,9 @@ func DecodeAccessTokenCustomClaims(accessToken string, e, n *string, customClaim
 		return rsaPublicKey, nil
 	})
 
-	return token2, err
+	if err != nil {
+		return nil, errors.Wrap(err, errMessage)
+	}
+
+	return token2, nil
 }


Test patch:
diff --git a/client_test.go b/client_test.go
index cb67f5b..cc62e24 100644
--- a/client_test.go
+++ b/client_test.go
@@ -252,6 +252,7 @@ func CreatePolicy(t *testing.T, client GoCloak, clientID string, policy PolicyRe
 		clientID,
 		policy)
 	assert.NoError(t, err, "CreatePolicy failed")
+
 	t.Logf("Created Policy ID: %s ", *(createdPolicy.ID))
 
 	tearDown := func() {
@@ -304,7 +305,9 @@ func SetUpTestUser(t testing.TB, client GoCloak) {
 			cfg.GoCloak.Realm,
 			user,
 		)
-		if IsObjectAlreadyExists(err) {
+
+		apiError, ok := err.(*APIError)
+		if ok && apiError.Code == http.StatusConflict {
 			users, err := client.GetUsers(
 				token.AccessToken,
 				cfg.GoCloak.Realm,
@@ -3337,3 +3340,23 @@ func TestGocloak_CreateListGetUpdateDeletePermission(t *testing.T) {
 	assert.NoError(t, err, "GetPermission failed")
 	assert.Equal(t, *(createdPermission.Name), *(updatedPermission.Name))
 }
+
+func TestGoCloak_CheckError(t *testing.T) {
+	t.Parallel()
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	token := GetAdminToken(t, client)
+
+	_, err := client.GetClient(token.AccessToken, cfg.Admin.Realm, "random_client")
+	assert.Error(t, err)
+
+	t.Log(err)
+
+	expectedError := &APIError{
+		Code:    http.StatusNotFound,
+		Message: "404 Not Found: Could not find client",
+	}
+
+	apiError := err.(*APIError)
+	assert.Equal(t, expectedError, apiError)
+}