yudai/gotty · error

Internal Server Error

Error message

Internal Server Error

What it means

GoTTY's wrapBasicAuth middleware in server/middleware.go:38 returns HTTP 500 'Internal Server Error' when the Base64 portion of the Authorization: Basic header cannot be decoded with base64.StdEncoding.DecodeString. This is a misclassification on the server's part: a malformed credential from the client is a client error (400/401), not a server fault. It means the header's payload contained characters outside the standard Base64 alphabet or had invalid length/padding.

Source

Thrown at server/middleware.go:38

		// todo add version
		w.Header().Set("Server", "GoTTY")
		handler.ServeHTTP(w, r)
	})
}

func (server *Server) wrapBasicAuth(handler http.Handler, credential string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := strings.SplitN(r.Header.Get("Authorization"), " ", 2)

		if len(token) != 2 || strings.ToLower(token[0]) != "basic" {
			w.Header().Set("WWW-Authenticate", `Basic realm="GoTTY"`)
			http.Error(w, "Bad Request", http.StatusUnauthorized)
			return
		}

		payload, err := base64.StdEncoding.DecodeString(token[1])
		if err != nil {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}

		if credential != string(payload) {
			w.Header().Set("WWW-Authenticate", `Basic realm="GoTTY"`)
			http.Error(w, "authorization failed", http.StatusUnauthorized)
			return
		}

		log.Printf("Basic Authentication Succeeded: %s", r.RemoteAddr)
		handler.ServeHTTP(w, r)
	})
}

View on GitHub (pinned to a080c85cbc)

Solutions

  1. Fix the client to send proper standard Base64 of 'username:password', e.g. curl -u user:pass or printf 'user:pass' | base64 (no wrapping/newlines)
  2. Check for and replace URL-safe base64 (-, _) with standard base64 (+, /) and correct padding in the token
  3. Verify no proxy or gateway is mangling the Authorization header; compare the header seen by the server (add logging) with what the client sends
  4. Patch the middleware to return http.StatusBadRequest or 401 with WWW-Authenticate instead of 500 for decode failures

Example fix

// before
token := r.Header.Get("Authorization") // Basic dXNlcnBhc3M  (unencoded / wrong alphabet)
// server responds 500 Internal Server Error

// after
import "encoding/base64"
header := base64.StdEncoding.EncodeToString([]byte("user:pass"))
req.Header.Set("Authorization", "Basic "+header) // Basic dXNlcjpwYXNz
Defensive patterns

Strategy: validation

Validate before calling

func validBasicHeader(h string) bool {
	parts := strings.SplitN(h, " ", 2)
	if len(parts) != 2 || !strings.EqualFold(parts[0], "Basic") {
		return false
	}
	_, err := base64.StdEncoding.DecodeString(strings.TrimSpace(parts[1]))
	return err == nil
}

Type guard

func isBase64(s string) bool {
	_, err := base64.StdEncoding.DecodeString(s)
	return err == nil
}

Prevention

When it happens

Trigger: A request carries 'Authorization: Basic <token>' where <token> is not valid standard Base64: URL-safe base64 used instead of standard (- and _ instead of + and /), missing '=' padding, whitespace or a trailing newline embedded in the token, a colon-containing raw 'user:pass' sent unencoded, or a token that was double-encoded/truncated by a proxy.

Common situations: Developers hand-crafting the header with `base64 -w0` vs raw encoding mismatches, clients using base64url (JWT-style) encoding, curl with a password containing special characters that break shell quoting, reverse proxies or API gateways re-encoding/stripping the Authorization header, or clients sending 'Basic' plus raw credentials without encoding at all.

Understand the failure class

Related errors


AI-assisted analysis of yudai/gotty@a080c85cbc (2026-09-02). Data as JSON: /api/errors/2e6ff4e5a7a80986. Report an issue: GitHub.