diff --git a/src/core/lt_core.c b/src/core/lt_core.c
index df9f58f..8c26ff3 100644
--- a/src/core/lt_core.c
+++ b/src/core/lt_core.c
@@ -4,12 +4,36 @@
 /*  Released under the Standard MIT License; see COPYING.SLTDL.    */
 /*******************************************************************/
 
+#include <dlfcn.h>
+#include <errno.h>
 #include <limits.h>
+#include <stdlib.h>
+#include <string.h>
 #include <pthread.h>
+
 #include <sltdl/sltdl.h>
+#include "sltdl_core.h"
+
+static const char * lt_dlerror_desc[] = {
+	[SLTDL_OK]                              = 0,
+	[SLTDL_SYSTEM_ERROR]                    = 0,
+	[SLTDL_DLFCN_ERROR]                     = 0,
+	[SLTDL_SLTDL_ERROR]                     = "sltdl internal error",
+	[SLTDL_DLEXIT_REF_COUNT]                = "lt_dlexit() reference count already zero",
+	[SLTDL_MODULE_REF_COUNT]                = "module reference count already zero",
+	[SLTDL_MODULE_PTR_INVALID]              = "module handle invalid",
+	[SLTDL_PATH_INVALID_FIRST_CHAR]         = "invalid path (does not begin with a forward slash)",
+	[SLTDL_PATH_INVALID_SEPARATTOR_CHAR]    = "invalid path (separator character is not a colon)",
+	[SLTDL_PATH_INVALID_MARK]               = "invalid path (mark not within range)",
+	[SLTDL_PATH_INVALID_LEN]                = "invalid path (string too long)",
+	[SLTDL_PATH_NO_ENTRY]                   = "invalid path (not found)",
+};
 
-static int             lt_refs = 0;
-static pthread_mutex_t lt_lock = PTHREAD_MUTEX_INITIALIZER;
+static int             lt_refs  = 0;
+static int             lt_error = 0;
+static int             lt_errno = 0;
+static char *          lt_dlerr = 0;
+static pthread_mutex_t lt_lock  = PTHREAD_MUTEX_INITIALIZER;
 
 int lt_dlinit(void)
 {
@@ -28,6 +52,7 @@ int lt_dlexit(void)
 		return 1;
 
 	if (!lt_refs) {
+		lt_error = SLTDL_DLEXIT_REF_COUNT;
 		pthread_mutex_unlock(&lt_lock);
 		return 1;
 	}
@@ -48,8 +73,57 @@ void lt_slock(void)
 	} while (locked);
 }
 
-int lt_sunlock(int ret)
+int lt_sunlock(int ret, int error)
 {
+	if (error ==  0) {
+		pthread_mutex_unlock(&lt_lock);
+		return 0;
+	}
+
+	if ((error < 0) || (error >= SLTDL_ERROR_CAP)) {
+		error = SLTDL_SLTDL_ERROR;
+
+	} else if (error == SLTDL_SYSTEM_ERROR) {
+		lt_errno = errno;
+
+	} else if (error == SLTDL_DLFCN_ERROR) {
+		if (lt_dlerr)
+			free(lt_dlerr);
+
+		lt_dlerr = strdup(dlerror());
+	}
+
+	lt_error = error;
 	pthread_mutex_unlock(&lt_lock);
 	return ret;
 }
+
+const char * lt_dlerror(void)
+{
+	const char * errdesc;
+
+	lt_slock();
+
+	switch (lt_error) {
+		case SLTDL_OK:
+			errdesc = 0;
+			break;
+
+		case SLTDL_SYSTEM_ERROR:
+			errdesc = strerror(lt_errno);
+			break;
+
+		case SLTDL_DLFCN_ERROR:
+			errdesc = lt_dlerr;
+			break;
+
+		default:
+			errdesc = lt_dlerror_desc[lt_error];
+			break;
+	}
+
+	lt_error = 0;
+	lt_sunlock(0,0);
+
+	return errdesc;
+}
diff --git a/src/core/lt_path.c b/src/core/lt_path.c
index 4981b1e..06252a4 100644
--- a/src/core/lt_path.c
+++ b/src/core/lt_path.c
@@ -17,6 +17,7 @@
 #include "sltdl_core.h"
 #include "sltdl_module.h"
 
+static int     lt_status;
 static off_t   lt_plen;
 static off_t   lt_plocs;
 static char *  lt_upath;
@@ -30,6 +31,12 @@ static struct lt_modctx * lt_modv_tail;
 static struct lt_modctx * lt_modv_next;
 static struct lt_modctx * lt_modv_cap;
 
+static int lt_setstatus(int ret, int status)
+{
+	lt_status = status;
+	return ret;
+}
+
 const char * lt_dlgetsearchpath(void)
 {
 	return lt_upath;
@@ -44,14 +51,14 @@ static int lt_dlsetsearchpath_locked(const char * path)
 	off_t           elements;
 
 	if (path[0] != '/')
-		return 1;
+		return lt_setstatus(1,SLTDL_PATH_INVALID_FIRST_CHAR);
 
 	elements = 1;
 
 	for (ch=&path[1]; *ch; ch++) {
 		if (*ch == ':') {
 			if (ch[1] != '/')
-				return 1;
+				return lt_setstatus(1,SLTDL_PATH_INVALID_FIRST_CHAR);
 
 			elements++;
 		}
@@ -69,7 +76,7 @@ static int lt_dlsetsearchpath_locked(const char * path)
 		lt_plocs ^= 0x3f;
 
 		if (!(lt_pathv = calloc(lt_plocs,sizeof(char *))))
-			return 1;
+			return lt_setstatus(1,SLTDL_SYSTEM_ERROR);
 	}
 
 	if ((ch - path) > lt_plen) {
@@ -84,9 +91,8 @@ static int lt_dlsetsearchpath_locked(const char * path)
 		lt_plen += 0x7ff;
 		lt_plen |= 0x7ff;
 		lt_plen ^= 0x7ff;
-
 		if (!(lt_upath = calloc(2*lt_plen,1)))
-			return 1;
+			return lt_setstatus(1,SLTDL_SYSTEM_ERROR);
 
 		lt_vpath = &lt_upath[lt_plen];
 	}
@@ -113,16 +119,18 @@ static int lt_dlsetsearchpath_locked(const char * path)
 
 	lt_vmark = pathv;
 
-	return 0;
+	return lt_setstatus(0,SLTDL_OK);
 }
 
 int lt_dlsetsearchpath(const char * path)
 {
+	int ret;
 	lt_slock();
-	return lt_sunlock(lt_dlsetsearchpath_locked(path));
+	ret = lt_dlsetsearchpath_locked(path);
+	return lt_sunlock(ret,lt_status);
 }
 
-int lt_dladdsearchdir(const char * path)
+static int lt_dladdsearchdir_locked(const char * path)
 {
 	int          ret;
 	const char * ch;
@@ -131,13 +139,11 @@ int lt_dladdsearchdir(const char * path)
 	off_t        plen;
 
 	if (path[0] != '/')
-		return 1;
+		return lt_setstatus(1,SLTDL_PATH_INVALID_FIRST_CHAR);
 
 	for (ch=path; *ch; ch++)
 		if (*ch == ':')
-			return 1;
-
-	lt_slock();
+			return lt_setstatus(1,SLTDL_PATH_INVALID_SEPARATTOR_CHAR);
 
 	alen = strlen(path);
 	plen = strlen(lt_upath);
@@ -154,12 +160,12 @@ int lt_dladdsearchdir(const char * path)
 
 		*lt_vmark++ = &lt_vpath[plen];
 
-		return lt_sunlock(0);
+		return lt_setstatus(0,SLTDL_OK);
 	}
 
 	/* (allocation needed) */
 	if (!(buf = malloc(plen + 1 + alen + 1)))
-		return lt_sunlock(1);
+		return lt_setstatus(1,SLTDL_SYSTEM_ERROR);
 
 	sprintf(buf,"%s:%s",lt_upath,path);
 
@@ -167,7 +173,15 @@ int lt_dladdsearchdir(const char * path)
 
 	free(buf);
 
-	return lt_sunlock(ret);
+	return ret;
+}
+
+int lt_dladdsearchdir(const char * path)
+{
+	int ret;
+	lt_slock();
+	ret = lt_dladdsearchdir_locked(path);
+	return lt_sunlock(ret,lt_status);
 }
 
 int lt_dlinsertsearchdir(const char * mark, const char * path)
@@ -182,26 +196,26 @@ int lt_dlinsertsearchdir(const char * mark, const char * path)
 	off_t        offset;
 	char **      pathv;
 
-	if (path[0] != '/')
-		return 1;
-
 	if (!mark)
 		return lt_dladdsearchdir(path);
 
+	lt_slock();
+
+	if (path[0] != '/')
+		return lt_sunlock(1,SLTDL_PATH_INVALID_FIRST_CHAR);
+
 	for (ch=path; *ch; ch++)
 		if (*ch == ':')
-			return 1;
-
-	lt_slock();
+			return lt_sunlock(1,SLTDL_PATH_INVALID_SEPARATTOR_CHAR);
 
 	alen = strlen(path);
 	plen = strlen(lt_upath);
 
 	if ((mark < lt_upath) || (mark >= &lt_upath[plen]))
-		return lt_sunlock(1);
+		return lt_sunlock(1,SLTDL_PATH_INVALID_MARK);
 
 	if ((mark > lt_upath) && (mark[-1] != ':'))
-		return lt_sunlock(1);
+		return lt_sunlock(1,SLTDL_PATH_INVALID_MARK);
 
 	mark = &lt_vpath[mark - lt_upath];
 
@@ -226,14 +240,14 @@ int lt_dlinsertsearchdir(const char * mark, const char * path)
 				memcpy(&lt_upath[offset],path,alen);
 				lt_upath[offset+alen] = ':';
 				lt_vmark++;
-				return lt_sunlock(0);
+				return lt_sunlock(0,SLTDL_OK);
 			}
 		}
 	}
 
 	/* (allocation needed) */
 	if (!(buf = malloc(plen + 1 + alen + 1)))
-		return lt_sunlock(1);
+		return lt_sunlock(1,SLTDL_SYSTEM_ERROR);
 
 	for (dst=buf, pathv=lt_pathv; *pathv; pathv++) {
 		if (*pathv == mark)
@@ -249,7 +263,7 @@ int lt_dlinsertsearchdir(const char * mark, const char * path)
 
 	free(buf);
 
-	return lt_sunlock(ret);
+	return lt_sunlock(ret,lt_status);
 }
 
 static int lt_dlpathopen_locked(
@@ -267,7 +281,7 @@ static int lt_dlpathopen_locked(
 	char          path[1024];
 
 	if ((mlen = strlen(module)) >= sizeof(path))
-		return -1;
+		return lt_setstatus(-1,SLTDL_PATH_INVALID_LEN);
 
 	memcpy(path,module,mlen);
 
@@ -278,7 +292,7 @@ static int lt_dlpathopen_locked(
 			for (pext=extv; *pext; pext++) {
 				if (mlen + (elen = strlen(*pext)) >= (sizeof(path))) {
 					close(fdat);
-					return (-1);
+					return lt_setstatus(-1,SLTDL_PATH_INVALID_LEN);
 				}
 
 				memcpy(&path[mlen],*pext,elen);
@@ -294,14 +308,14 @@ static int lt_dlpathopen_locked(
 						if (!(*mpath = malloc(plen))) {
 							close(fdat);
 							close(fdmod);
-							return (-1);
+							return lt_setstatus(-1,SLTDL_SYSTEM_ERROR);
 						}
 
 						sprintf(*mpath,"%s/%s",*ppath,path);
 					}
 
 					close(fdat);
-					return fdmod;
+					return lt_setstatus(fdmod,SLTDL_OK);
 				}
 			}
 
@@ -309,13 +323,15 @@ static int lt_dlpathopen_locked(
 		}
 	}
 
-	return -1;
+	return lt_setstatus(-1,SLTDL_PATH_NO_ENTRY);
 }
 
 int lt_dlpathopen(const char * module, const char ** extv)
 {
+	int ret;
 	lt_slock();
-	return lt_sunlock(lt_dlpathopen_locked(module,extv,0));
+	ret = lt_dlpathopen_locked(module,extv,0);
+	return lt_sunlock(ret,lt_status);
 }
 
 static struct lt_modctx * lt_dlopen_locked(
@@ -339,6 +355,7 @@ static struct lt_modctx * lt_dlopen_locked(
 	if (lt_modv_next == lt_modv_cap) {
 		if (!(modctx_buf = calloc(64,sizeof(*modctx)))) {
 			free(mpath);
+			lt_setstatus(0,SLTDL_SYSTEM_ERROR);
 			return 0;
 		}
 
@@ -349,6 +366,7 @@ static struct lt_modctx * lt_dlopen_locked(
 	/* dlopen */
 	if (!(maddr = dlopen(mpath,mode))) {
 		free(mpath);
+		lt_setstatus(0,SLTDL_DLFCN_ERROR);
 		return 0;
 	}
 
@@ -388,7 +406,7 @@ struct lt_modctx * lt_dlopen(const char * module)
 
 	lt_slock();
 	modctx = lt_dlopen_locked(module,extv,RTLD_NOW);
-	lt_sunlock(0);
+	lt_sunlock(0,lt_status);
 	return modctx;
 }
 
@@ -399,7 +417,7 @@ struct lt_modctx * lt_dlopenext(const char * module)
 
 	lt_slock();
 	modctx = lt_dlopen_locked(module,extv,RTLD_NOW);
-	lt_sunlock(0);
+	lt_sunlock(0,lt_status);
 	return modctx;
 }
 
@@ -424,12 +442,12 @@ int lt_dlclose(struct lt_modctx * modctx)
 		if (pmod == modctx) {
 			if (pmod->mrefs) {
 				pmod->mrefs--;
-				return lt_sunlock(0);
+				return lt_sunlock(0,SLTDL_OK);
 			}
 
-			return lt_sunlock(-1);
+			return lt_sunlock(-1,SLTDL_MODULE_REF_COUNT);
 		}
 	}
 
-	return lt_sunlock(-1);
+	return lt_sunlock(-1,SLTDL_MODULE_PTR_INVALID);
 }
diff --git a/src/internal/sltdl_core.h b/src/internal/sltdl_core.h
index f74542e..35e1a24 100644
--- a/src/internal/sltdl_core.h
+++ b/src/internal/sltdl_core.h
@@ -1,7 +1,23 @@
 #ifndef SLTDL_CORE_H
 #define SLTDL_CORE_H
 
+enum sltdl_error {
+	SLTDL_OK,
+	SLTDL_SYSTEM_ERROR,
+	SLTDL_DLFCN_ERROR,
+	SLTDL_SLTDL_ERROR,
+	SLTDL_DLEXIT_REF_COUNT,
+	SLTDL_MODULE_REF_COUNT,
+	SLTDL_MODULE_PTR_INVALID,
+	SLTDL_PATH_INVALID_FIRST_CHAR,
+	SLTDL_PATH_INVALID_SEPARATTOR_CHAR,
+	SLTDL_PATH_INVALID_MARK,
+	SLTDL_PATH_INVALID_LEN,
+	SLTDL_PATH_NO_ENTRY,
+	SLTDL_ERROR_CAP,
+};
+
 void lt_slock(void);
-int  lt_sunlock(int);
+int  lt_sunlock(int,int);
 
 #endif