diff --git a/src/sbbs3/sftp.cpp b/src/sbbs3/sftp.cpp
index 6a298235bf29f79e1f2fe82cb2d39112cc57d811..069d768c9b90beb2b24a95bdadccbbf360ee8200 100644
--- a/src/sbbs3/sftp.cpp
+++ b/src/sbbs3/sftp.cpp
@@ -1248,17 +1248,28 @@ sftp_send(uint8_t *buf, size_t len, void *cb_data)
 }
 
 static void
-sftp_lprintf(void *arg, const char *fmt, ...)
+sftp_lprintf(void *arg, uint32_t errcode, const char *fmt, ...)
 {
 	sbbs_t *sbbs = (sbbs_t *)arg;
 	va_list argptr;
 	char sbuf[1024];
+	int level = LOG_DEBUG;
+
+	switch (errcode) {
+		case SSH_FX_PERMISSION_DENIED:
+			level = LOG_INFO;
+			break;
+		case SSH_FX_FAILURE:
+		case SSH_FX_BAD_MESSAGE:
+			level = LOG_WARNING;
+			break;
+	}
 
 	va_start(argptr,fmt);
 	vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
 	sbuf[sizeof(sbuf)-1]=0;
 	va_end(argptr);
-	sbbs->lprintf(LOG_ERR, "SFTP %s", sbuf);
+	sbbs->lprintf(level, "SFTP error code %" PRIu32 " (%s) %s", errcode, sftp_get_errcode_name(errcode), sbuf);
 }
 
 static void
diff --git a/src/sftp/sftp.h b/src/sftp/sftp.h
index 172d0f26f39fe8ed549489d858d7d59ca6af29c1..09d1da4bc3ad23b04dd200f0bf5f174d81e6aeed 100644
--- a/src/sftp/sftp.h
+++ b/src/sftp/sftp.h
@@ -193,7 +193,7 @@ typedef struct sftp_server_state {
 	sftp_rx_pkt_t rxp;
 	sftp_tx_pkt_t txp;
 	void *cb_data;
-	void (*lprintf)(void *cb_data, const char *fmt, ...);
+	void (*lprintf)(void *cb_data, uint32_t errcode, const char *fmt, ...);
 	void (*cleanup_callback)(void *cb_data);
 	bool (*open)(sftp_str_t filename, uint32_t flags, sftp_file_attr_t attributes, void *cb_data);
 	bool (*close)(sftp_str_t handle, void *cb_data);
@@ -223,6 +223,7 @@ typedef struct sftp_server_state {
 
 /* sftp_pkt.c */
 const char * const sftp_get_type_name(uint8_t type);
+const char * const sftp_get_errcode_name(uint32_t errcode);
 bool sftp_have_pkt_sz(sftp_rx_pkt_t pkt);
 bool sftp_have_pkt_type(sftp_rx_pkt_t pkt);
 uint32_t sftp_pkt_sz(sftp_rx_pkt_t pkt);
diff --git a/src/sftp/sftp_pkt.c b/src/sftp/sftp_pkt.c
index e65ea0a17ba4c665ae22d9f1d054f494d7cd918c..7f15fd797a00dda0e9462b2c4f0abee2f4cac01e 100644
--- a/src/sftp/sftp_pkt.c
+++ b/src/sftp/sftp_pkt.c
@@ -45,6 +45,21 @@ static const struct type_names {
 };
 static const char * const notfound_type = "<UNKNOWN>";
 
+static const struct errcode_names {
+	const uint32_t errcode;
+	const char * const name;
+} errcode_names[] = {
+	{SSH_FX_OK, "OK"},
+	{SSH_FX_EOF, "End Of File"},
+	{SSH_FX_NO_SUCH_FILE, "No Such File"},
+	{SSH_FX_PERMISSION_DENIED, "Permission Denied"},
+	{SSH_FX_FAILURE, "General Failure"},
+	{SSH_FX_BAD_MESSAGE, "Bad Message"},
+	{SSH_FX_NO_CONNECTION, "No Connection"},
+	{SSH_FX_CONNECTION_LOST, "Connection Lost"},
+	{SSH_FX_OP_UNSUPPORTED, "Operation Unsupported"},
+};
+
 static int type_cmp(const void *key, const void *name)
 {
 	int k = *(uint8_t *)key;
@@ -63,6 +78,24 @@ sftp_get_type_name(uint8_t type)
 	return t->name;
 }
 
+static int errcode_cmp(const void *key, const void *name)
+{
+	int k = *(uint32_t *)key;
+	int n = *(uint32_t *)name;
+
+	return k - n;
+}
+
+const char * const
+sftp_get_errcode_name(uint32_t errcode)
+{
+	struct errcode_names *ec = (struct errcode_names *)(bsearch(&errcode, errcode_names, sizeof(errcode_names) / sizeof(errcode_names[0]), sizeof(errcode_names[0]), errcode_cmp));
+
+	if (ec == NULL)
+		return notfound_type;
+	return ec->name;
+}
+
 bool
 sftp_have_pkt_sz(sftp_rx_pkt_t pkt)
 {
diff --git a/src/sftp/sftp_server.c b/src/sftp/sftp_server.c
index 1d047b22a36c9f117fe63df0a4ea075f1f6270e8..d4ee26aaf2696c1a2901787c4e6475523329fe39 100644
--- a/src/sftp/sftp_server.c
+++ b/src/sftp/sftp_server.c
@@ -329,6 +329,8 @@ sftps_send_packet(sftps_state_t state)
 bool
 sftps_send_error(sftps_state_t state, uint32_t code, const char *msg)
 {
+	if (state->lprintf)
+		state->lprintf(state->cb_data, code, "%s", msg);
 	if (!appendheader(state, SSH_FXP_STATUS))
 		return false;
 	if (!append32(state, code))
@@ -373,7 +375,8 @@ sftps_recv(sftps_state_t state, uint8_t *buf, uint32_t sz)
 	if (sftp_have_pkt_sz(state->rxp)) {
 		uint32_t psz = sftp_pkt_sz(state->rxp);
 		if (psz > SFTP_MAX_PACKET_SIZE) {
-			state->lprintf(state->cb_data, "Packet too large (%" PRIu32 " bytes)", psz);
+			if (state->lprintf)
+				state->lprintf(state->cb_data, SSH_FX_FAILURE, "Packet too large (%" PRIu32 " bytes)", psz);
 			return exit_function(state, false);
 		}
 	}
@@ -526,7 +529,7 @@ sftps_recv(sftps_state_t state, uint8_t *buf, uint32_t sz)
 		}
 		if (!handled) {
 			if (state->lprintf)
-				state->lprintf(state->cb_data, "Unhandled request type: %s (%d)", sftp_get_type_name(state->rxp->type), state->rxp->type);
+				state->lprintf(state->cb_data, SSH_FX_FAILURE, "Unhandled request type: %s (%d)", sftp_get_type_name(state->rxp->type), state->rxp->type);
 			state->id = get32(state);
 			if (!sftps_send_error(state, SSH_FX_OP_UNSUPPORTED, "Operation not implemented"))
 				return exit_function(state, false);
@@ -565,19 +568,22 @@ sftps_send_name(sftps_state_t state, uint32_t count, str_list_t fnames, str_list
 		return false;
 	for (uint32_t idx = 0; idx < count; idx++) {
 		if (fnames[idx] == NULL) {
-			state->lprintf(state->cb_data, "Reached fnames terminator at position %" PRIu32 " of " PRIu32, idx, count);
+			if (state->lprintf)
+				state->lprintf(state->cb_data, SSH_FX_FAILURE, "Reached fnames terminator at position %" PRIu32 " of " PRIu32, idx, count);
 			return false;
 		}
 		if (!appendcstring(state, fnames[idx]))
 			return false;
 		if (lnames[idx] == NULL) {
-			state->lprintf(state->cb_data, "Reached lnames terminator at position %" PRIu32 " of " PRIu32, idx, count);
+			if (state->lprintf)
+				state->lprintf(state->cb_data, SSH_FX_FAILURE, "Reached lnames terminator at position %" PRIu32 " of " PRIu32, idx, count);
 			return false;
 		}
 		if (!appendcstring(state, lnames[idx]))
 			return false;
 		if (attrs[idx] == NULL) {
-			state->lprintf(state->cb_data, "Reached attrs terminator at position %" PRIu32 " of " PRIu32, idx, count);
+			if (state->lprintf)
+				state->lprintf(state->cb_data, SSH_FX_FAILURE, "Reached attrs terminator at position %" PRIu32 " of " PRIu32, idx, count);
 			return false;
 		}
 		if (!appendfattr(state, attrs[idx]))