diff --git a/src/sbbs3/js_socket.c b/src/sbbs3/js_socket.c
index a8f2eb1e77848c3a012306c01995ff7812039c7f..ba5cfaf887491694551828d1dddcd3a58e2c740b 100644
--- a/src/sbbs3/js_socket.c
+++ b/src/sbbs3/js_socket.c
@@ -75,6 +75,15 @@ static JSBool js_install_event(JSContext *cx, uintN argc, jsval *arglist, bool o
 static JSBool js_on(JSContext *cx, uintN argc, jsval *arglist);
 static JSBool js_once(JSContext *cx, uintN argc, jsval *arglist);
 
+static void store_socket_error(js_socket_private_t* p, int error_num, const char* error_str)
+{
+	p->last_error = error_num;
+	if(error_str != NULL)
+		strlcpy(p->last_error_str, error_str, sizeof p->last_error_str);
+	else
+		socket_strerror(error_num, p->last_error_str, sizeof p->last_error_str);
+}
+
 static int do_cryptAttribute(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, int val)
 {
 	int ret;
@@ -242,7 +251,7 @@ static void do_js_close(JSContext *cx, js_socket_private_t *p, bool finalize)
 	}
 	if(p->external==false) {
 		close_socket(p->sock);
-		p->last_error = ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 	}
 	else {
 		if (!finalize)
@@ -771,13 +780,13 @@ js_bind(JSContext *cx, uintN argc, jsval *arglist)
 	if((ret=getaddrinfo(cstr, portstr, &hints, &res)) != 0) {
 		JS_RESUMEREQUEST(cx,rc);
 		dbprintf(TRUE, p, "getaddrinfo(%s, %s) failed with error %d", cstr, portstr, ret);
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ret, gai_strerror(ret));
 		return(JS_TRUE);
 	}
 	for(tres=res; tres; tres=tres->ai_next) {
 		if(bind(p->sock, tres->ai_addr, tres->ai_addrlen)!=0) {
 			if (tres->ai_next == NULL) {
-				p->last_error=ERROR_VALUE;
+				store_socket_error(p, ERROR_VALUE, NULL);
 				dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE);
 				freeaddrinfo(res);
 				JS_RESUMEREQUEST(cx, rc);
@@ -815,7 +824,7 @@ js_listen(JSContext *cx, uintN argc, jsval *arglist)
 
 	rc=JS_SUSPENDREQUEST(cx);
 	if(listen(p->sock, backlog)!=0) {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		dbprintf(TRUE, p, "listen failed with error %d",ERROR_VALUE);
 		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
 		JS_RESUMEREQUEST(cx, rc);
@@ -850,7 +859,7 @@ js_accept(JSContext *cx, uintN argc, jsval *arglist)
 	rc=JS_SUSPENDREQUEST(cx);
 	if(p->set) {
 		if((new_socket=xpms_accept(p->set,&(p->remote_addr),&addrlen,XPMS_FOREVER,XPMS_FLAGS_NONE,NULL))==INVALID_SOCKET) {
-			p->last_error=ERROR_VALUE;
+			store_socket_error(p, ERROR_VALUE, NULL);
 			dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE);
 			JS_RESUMEREQUEST(cx, rc);
 			return(JS_TRUE);
@@ -859,7 +868,7 @@ js_accept(JSContext *cx, uintN argc, jsval *arglist)
 	}
 	else {
 		if((new_socket=accept_socket(p->sock,&(p->remote_addr),&addrlen))==INVALID_SOCKET) {
-			p->last_error=ERROR_VALUE;
+			store_socket_error(p, ERROR_VALUE, NULL);
 			dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE);
 			JS_RESUMEREQUEST(cx, rc);
 			return(JS_TRUE);
@@ -1068,7 +1077,7 @@ js_connect(JSContext *cx, uintN argc, jsval *arglist)
 	result = getaddrinfo(p->hostname, NULL, &hints, &res);
 	if(result != 0) {
 		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
-		p->last_error = ERROR_VALUE;
+		store_socket_error(p, result, gai_strerror(result));
 		dbprintf(TRUE, p, "getaddrinfo(%s) failed with error %d", p->hostname, result);
 		JS_RESUMEREQUEST(cx, rc);
 		return(JS_TRUE);
@@ -1109,7 +1118,7 @@ js_connect(JSContext *cx, uintN argc, jsval *arglist)
 
 	if(result!=0) {
 		freeaddrinfo(res);
-		p->last_error = result;
+		store_socket_error(p, result, NULL);
 		dbprintf(TRUE, p, "connect failed with error %d", result);
 		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
 		JS_RESUMEREQUEST(cx, rc);
@@ -1156,7 +1165,7 @@ js_send(JSContext *cx, uintN argc, jsval *arglist)
 		dbprintf(false, p, "sent %d of %lu bytes",ret,len);
 		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(ret));
 	} else {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		dbprintf(TRUE, p, "send of %lu bytes failed",len);
 	}
 	free(cp);
@@ -1195,7 +1204,7 @@ js_sendline(JSContext *cx, uintN argc, jsval *arglist)
 		dbprintf(false, p, "sent %lu bytes",len+2);
 		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
 	} else {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		dbprintf(TRUE, p, "send of %lu bytes failed",len+2);
 	}
 	free(cp);
@@ -1257,7 +1266,7 @@ js_sendto(JSContext *cx, uintN argc, jsval *arglist)
 	dbprintf(false, p, "resolving hostname: %s", p->hostname);
 
 	if((result=getaddrinfo(p->hostname, NULL, &hints, &res) != 0)) {
-		p->last_error = ERROR_VALUE;
+		store_socket_error(p, result, gai_strerror(result));
 		dbprintf(TRUE, p, "getaddrinfo(%s) failed with error %d", p->hostname, result);
 		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
 		free(cp);
@@ -1274,7 +1283,7 @@ js_sendto(JSContext *cx, uintN argc, jsval *arglist)
 			dbprintf(false, p, "sent %lu bytes",len);
 			JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
 		} else {
-			p->last_error=ERROR_VALUE;
+			store_socket_error(p, ERROR_VALUE, NULL);
 			dbprintf(TRUE, p, "send of %lu bytes failed to %s",len, ip_addr);
 		}
 	}
@@ -1325,7 +1334,7 @@ js_sendfile(JSContext *cx, uintN argc, jsval *arglist)
 		dbprintf(false, p, "sent %"PRIdOFF" bytes",len);
 		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
 	} else {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		dbprintf(TRUE, p, "send of %s failed",fname);
 	}
 	free(fname);
@@ -1386,7 +1395,7 @@ js_sendbin(JSContext *cx, uintN argc, jsval *arglist)
 		dbprintf(false, p, "sent %u bytes (binary)",size);
 		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
 	} else {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		dbprintf(TRUE, p, "send of %u bytes (binary) failed",size);
 	}
 
@@ -1430,7 +1439,7 @@ js_recv(JSContext *cx, uintN argc, jsval *arglist)
 	len = js_socket_recv(cx,p,buf,len,0,timeout);
 	JS_RESUMEREQUEST(cx, rc);
 	if(len<0) {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
 		free(buf);
 		return(JS_TRUE);
@@ -1522,7 +1531,7 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist)
 		JS_RESUMEREQUEST(cx, rc);
 
 		if(rd!=len) {
-			p->last_error=ERROR_VALUE;
+			store_socket_error(p, ERROR_VALUE, NULL);
 			return(JS_TRUE);
 		}
 
@@ -1537,7 +1546,7 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist)
 		len = recvfrom(p->sock,buf,len,0,&addr.addr,&addrlen);
 		JS_RESUMEREQUEST(cx, rc);
 		if(len<0) {
-			p->last_error=ERROR_VALUE;
+			store_socket_error(p, ERROR_VALUE, NULL);
 			free(buf);
 			return(JS_TRUE);
 		}
@@ -1617,7 +1626,7 @@ js_peek(JSContext *cx, uintN argc, jsval *arglist)
 		len=0;
 	JS_RESUMEREQUEST(cx, rc);
 	if(len<0) {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
 		free(buf);
 		return(JS_TRUE);
@@ -1653,7 +1662,7 @@ js_sock_read_check(js_socket_private_t *p, time_t start, int32 timeout, int i)
 	}
 
 	if(!socket_check(p->sock,&rd,NULL,1000)) {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		return 2;
 	}
 
@@ -1722,7 +1731,7 @@ js_recvline(JSContext *cx, uintN argc, jsval *arglist)
 
 		if((got=js_socket_recv(cx, p, &ch, 1, 0, i?1:timeout))!=1) {
 			if(p->session == -1)
-				p->last_error = ERROR_VALUE;
+				store_socket_error(p, ERROR_VALUE, NULL);
 			if (i == 0) {			// no data received
 				JS_RESUMEREQUEST(cx, rc);
 				free(buf);			// so return null (not an empty string)
@@ -1801,7 +1810,7 @@ js_recvbin(JSContext *cx, uintN argc, jsval *arglist)
 	}
 
 	if(rd!=size)
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 
 	JS_RESUMEREQUEST(cx, rc);
 	return(JS_TRUE);
@@ -1849,7 +1858,7 @@ js_getsockopt(JSContext *cx, uintN argc, jsval *arglist)
 		}
 		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(val));
 	} else {
-		p->last_error=ERROR_VALUE;
+		store_socket_error(p, ERROR_VALUE, NULL);
 		dbprintf(TRUE, p, "error %d getting option %d"
 			,ERROR_VALUE,opt);
 	}
@@ -1904,7 +1913,7 @@ js_setsockopt(JSContext *cx, uintN argc, jsval *arglist)
 
 	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(
 		setsockopt(p->sock, level, opt, vp, len)==0));
-	p->last_error=ERROR_VALUE;
+	store_socket_error(p, ERROR_VALUE, NULL);
 
 	JS_RESUMEREQUEST(cx, rc);
 	return(JS_TRUE);
@@ -1941,7 +1950,7 @@ js_ioctlsocket(JSContext *cx, uintN argc, jsval *arglist)
 		JS_RESUMEREQUEST(cx, rc);
 	}
 
-	p->last_error=ERROR_VALUE;
+	store_socket_error(p, ERROR_VALUE, NULL);
 
 	return(JS_TRUE);
 }
@@ -2036,7 +2045,7 @@ js_poll(JSContext *cx, uintN argc, jsval *arglist)
 		result = select(high+1,rd_set,wr_set,NULL,&tv);
 #endif
 
-	p->last_error=ERROR_VALUE;
+	store_socket_error(p, ERROR_VALUE, NULL);
 
 	dbprintf(false, p, "poll: select/poll returned %d (errno %d)"
 		,result,p->last_error);
@@ -2462,7 +2471,7 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 			*vp = INT_TO_JSVAL(p->last_error);
 			break;
 		case SOCK_PROP_ERROR_STR:
-			if((js_str=JS_NewStringCopyZ(cx, socket_strerror(p->last_error, str, sizeof(str))))==NULL)
+			if((js_str=JS_NewStringCopyZ(cx, p->last_error_str))==NULL)
 				return JS_FALSE;
 			*vp = STRING_TO_JSVAL(js_str);
 			break;
diff --git a/src/sbbs3/js_socket.h b/src/sbbs3/js_socket.h
index a42da28d75f23ed10081a1b45d98d1bd61f61baf..81719a36c4c6762631552301ff3efbbb46b433a6 100644
--- a/src/sbbs3/js_socket.h
+++ b/src/sbbs3/js_socket.h
@@ -15,6 +15,7 @@ typedef struct
 	BOOL	is_connected;
 	BOOL	network_byte_order;
 	int		last_error;
+	char	last_error_str[128];
 	int		type;
 	union xp_sockaddr	remote_addr;
 	CRYPT_SESSION	session;