diff --git a/plugins/ui/files.go b/plugins/ui/files.go
index 9af8e513f637c38ad2935bbce8db0d91c746fb06..9bd67d4be25af9bcfed545c7bacd8f76e9d0dab0 100644
--- a/plugins/ui/files.go
+++ b/plugins/ui/files.go
@@ -241,7 +241,10 @@ body.fade {
                 <span>Password:</span>
                 <input class="input" placeholder="Username" name="password">
               </div>
-              <button type="submit" class="button is-primary">Login</button>
+              <button type="submit" :disabled="loginError?true:false"
+                :class="loginError?'button is-danger':'button is-primary'">
+                {{ loginButtonText }}
+              </button>
             </form>
           </div>
 
@@ -452,6 +455,7 @@ Vue.component('empty-icon', {
   info: {},
   selectedTxHash:null,
   tps:0,
+  loginError:'',
 }
 `,
 	"js/main.js":`new Vue({
@@ -469,9 +473,14 @@ Vue.component('empty-icon', {
       const formData = new FormData(e.target);
       const pass = formData.get('password')
       const user = formData.get('username')
-      const r = await api.get('login?username='+user+'&password='+pass)
-      if(r.token){
-        this.init()
+      try{
+        const r = await api.get('login?username='+user+'&password='+pass)
+        if(r.token){
+          this.init()
+        }
+      }catch(e){
+        this.loginError = 'Not Authorized'
+        setTimeout(()=>this.loginError='',2000)
       }
     },
     async init() {
@@ -499,11 +508,11 @@ Vue.component('empty-icon', {
     },
     footerContainerStyle() {
       const size = Math.max(window.innerHeight-this.headerSize, 300)
-      return 'height:calc('+(size-1 )+'px - 3.6rem);'
+      return 'height:calc('+(size-1)+'px - 3.6rem);'
     },
     startSpam() {
       console.log('start spam', this.tpsToSpam)
-      api.get('spammer?cmd=start&tps='+this.tpsToSpam+')')
+      api.get('spammer?cmd=start&tps='+this.tpsToSpam)
     },
     stopSpam() {
       console.log('stop spam')
@@ -576,6 +585,9 @@ Vue.component('empty-icon', {
     infoKeys() {
       return ['TPS', 'Node ID', 'Neighbors', 'Peers', 'Uptime']
     },
+    loginButtonText() {
+      return this.loginError || 'Login'
+    },
     infoValues() {
       const i = this.info
       return i.id ? [
@@ -922,23 +934,37 @@ const logLevels = [{
 const api = {
   get: async function(u) {
     const url = this.trim('/'+u)
-    const r = await fetch(this.addToken(url))
-    const j = await r.json()
-    if(url.startsWith('login') && j.token) {
-      localStorage.setItem('token', j.token)
-      await sleep(1)
+    try{
+      const r = await fetch(this.addToken(url))
+      const j = await r.json()
+      if (!(r.status >= 200 && r.status < 300)) {
+        throw new Error(j)
+      }
+      if(url.startsWith('login') && j.token) {
+        localStorage.setItem('token', j.token)
+        await sleep(1)
+      }
+      return j
+    } catch(e) {
+      throw e
     }
-    return j
   },
   post: async function(u, data){
     const url = this.trim('/'+u)
-    const r = await fetch(this.addToken(url), {
-      method: 'POST',
-      headers: {'Content-Type': 'application/json'},
-      data: JSON.stringify(data)
-    })
-    const j = await r.json()
-    return j
+    try{
+      const r = await fetch(this.addToken(url), {
+        method: 'POST',
+        headers: {'Content-Type': 'application/json'},
+        data: JSON.stringify(data)
+      })
+      const j = await r.json()
+      if (!(r.status >= 200 && r.status < 300)) {
+        throw new Error(j)
+      }
+      return j
+    } catch(e) {
+      throw e
+    }
   },
   trim: function(s) {
     return s.replace(/^\//, '');
diff --git a/plugins/ui/src/index.html b/plugins/ui/src/index.html
index 92448db2d841841522b5ceaa240bdacea1ca51ef..b3825fe06ea35507bd540845894c64578f8f702f 100644
--- a/plugins/ui/src/index.html
+++ b/plugins/ui/src/index.html
@@ -33,7 +33,10 @@
                 <span>Password:</span>
                 <input class="input" placeholder="Username" name="password">
               </div>
-              <button type="submit" class="button is-primary">Login</button>
+              <button type="submit" :disabled="loginError?true:false"
+                :class="loginError?'button is-danger':'button is-primary'">
+                {{ loginButtonText }}
+              </button>
             </form>
           </div>
 
diff --git a/plugins/ui/src/js/initials.js b/plugins/ui/src/js/initials.js
index 93157c1ac6520edd8e3ccf0a6b551816bce889bb..d059a9d28337a0d42405b7311b6535070892bb1a 100644
--- a/plugins/ui/src/js/initials.js
+++ b/plugins/ui/src/js/initials.js
@@ -10,4 +10,5 @@ var initialData = {
   info: {},
   selectedTxHash:null,
   tps:0,
+  loginError:'',
 }
diff --git a/plugins/ui/src/js/main.js b/plugins/ui/src/js/main.js
index c007ef90aa812bdb55f9e78467b22bb50f5ed47f..17f5a38827ef8d4922d40a3d6bcc4c3be4303dd5 100644
--- a/plugins/ui/src/js/main.js
+++ b/plugins/ui/src/js/main.js
@@ -13,9 +13,14 @@ new Vue({
       const formData = new FormData(e.target);
       const pass = formData.get('password')
       const user = formData.get('username')
-      const r = await api.get('login?username='+user+'&password='+pass)
-      if(r.token){
-        this.init()
+      try{
+        const r = await api.get('login?username='+user+'&password='+pass)
+        if(r.token){
+          this.init()
+        }
+      }catch(e){
+        this.loginError = 'Not Authorized'
+        setTimeout(()=>this.loginError='',2000)
       }
     },
     async init() {
@@ -43,11 +48,11 @@ new Vue({
     },
     footerContainerStyle() {
       const size = Math.max(window.innerHeight-this.headerSize, 300)
-      return 'height:calc('+(size-1 )+'px - 3.6rem);'
+      return 'height:calc('+(size-1)+'px - 3.6rem);'
     },
     startSpam() {
       console.log('start spam', this.tpsToSpam)
-      api.get('spammer?cmd=start&tps='+this.tpsToSpam+')')
+      api.get('spammer?cmd=start&tps='+this.tpsToSpam)
     },
     stopSpam() {
       console.log('stop spam')
@@ -120,6 +125,9 @@ new Vue({
     infoKeys() {
       return ['TPS', 'Node ID', 'Neighbors', 'Peers', 'Uptime']
     },
+    loginButtonText() {
+      return this.loginError || 'Login'
+    },
     infoValues() {
       const i = this.info
       return i.id ? [
diff --git a/plugins/ui/src/js/utils.js b/plugins/ui/src/js/utils.js
index d61b55e13d864739b609cbbfe12e26dd8fe5cef3..5f99f30f5cca451b44d4ad4fe4ee122c087adac7 100644
--- a/plugins/ui/src/js/utils.js
+++ b/plugins/ui/src/js/utils.js
@@ -36,23 +36,37 @@ const logLevels = [{
 const api = {
   get: async function(u) {
     const url = this.trim('/'+u)
-    const r = await fetch(this.addToken(url))
-    const j = await r.json()
-    if(url.startsWith('login') && j.token) {
-      localStorage.setItem('token', j.token)
-      await sleep(1)
+    try{
+      const r = await fetch(this.addToken(url))
+      const j = await r.json()
+      if (!(r.status >= 200 && r.status < 300)) {
+        throw new Error(j)
+      }
+      if(url.startsWith('login') && j.token) {
+        localStorage.setItem('token', j.token)
+        await sleep(1)
+      }
+      return j
+    } catch(e) {
+      throw e
     }
-    return j
   },
   post: async function(u, data){
     const url = this.trim('/'+u)
-    const r = await fetch(this.addToken(url), {
-      method: 'POST',
-      headers: {'Content-Type': 'application/json'},
-      data: JSON.stringify(data)
-    })
-    const j = await r.json()
-    return j
+    try{
+      const r = await fetch(this.addToken(url), {
+        method: 'POST',
+        headers: {'Content-Type': 'application/json'},
+        data: JSON.stringify(data)
+      })
+      const j = await r.json()
+      if (!(r.status >= 200 && r.status < 300)) {
+        throw new Error(j)
+      }
+      return j
+    } catch(e) {
+      throw e
+    }
   },
   trim: function(s) {
     return s.replace(/^\//, '');