juanfont/headscale · error
MOCKOIDC_CLIENT_ID not defined
Error message
MOCKOIDC_CLIENT_ID not defined
What it means
This is headscale's generic 500 response emitted by the httpError helper whenever a handler returns a Go error that is not an HTTPError (errors.AsType[HTTPError] fails). The real cause is not in the response body by design — it is logged server-side via zerolog with 'http internal server error', the error, and code 500.
Source
Thrown at cmd/headscale/cli/mockoidc.go:21
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"os"
"strconv"
"time"
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
"github.com/oauth2-proxy/mockoidc"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
var (
errMockOidcClientIDNotDefined = errors.New("MOCKOIDC_CLIENT_ID not defined")
errMockOidcClientSecretNotDefined = errors.New("MOCKOIDC_CLIENT_SECRET not defined")
errMockOidcPortNotDefined = errors.New("MOCKOIDC_PORT not defined")
errMockOidcUsersNotDefined = errors.New("MOCKOIDC_USERS not defined")
)
const refreshTTL = 60 * time.Minute
var accessTTL = 2 * time.Minute
func init() {
rootCmd.AddCommand(mockOidcCmd)
}
var mockOidcCmd = &cobra.Command{
Use: "mockoidc",
Short: "Runs a mock OIDC server for testing",
Long: "This internal command runs a OpenID Connect for testing purposes",
RunE: func(cmd *cobra.Command, args []string) error {View on GitHub (pinned to 565fd254d0)
Solutions
- Read the server log for the matching line: log.Error().Err(err).Int("code",500).Msg("http internal server error") — the Err field holds the actual cause; fix that root cause.
- Check database health: SQLite file permissions/disk space, or PostgreSQL connectivity and migrations being up to date.
- If you control the calling code, wrap the failure in an HTTPError (via the HTTPError type) so the client gets a meaningful status/message instead of the generic 500.
- Reproduce with headscale serve in the foreground to capture the log line while issuing the failing request.
Example fix
// before: caller gets an opaque 500
httpError(w, fmt.Errorf("lookup node %q: %w", name, err))
// after: surface a typed, loggable error
var HTTPErrorNotFound = NewHTTPError(http.StatusNotFound, err, fmt.Sprintf("node %q not found", name))
httpError(w, HTTPErrorNotFound) Defensive patterns
Strategy: retry
Try / catch
resp, err := client.Do(req)
if err == nil && resp.StatusCode == http.StatusInternalServerError {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
// generic 500s here are non-specific: log correlation ID/time and check server zerolog output
log.Printf("headscale 500 at %s: server log holds the cause (Err field)", time.Now().UTC())
_ = body
} Prevention
- Watch the headscale log whenever you see a bare 'internal server error' — the response intentionally hides the cause.
- Monitor DB health (SQLite disk space/permissions, PostgreSQL reachability) to prevent the most common root causes.
- In your own handlers, always wrap errors in HTTPError so clients get actionable statuses instead of 500.
When it happens
Trigger: Any handler calling httpError(w, err) with a plain error: JSON marshal failures in writeJSON, database/GORM errors surfacing from state or db layers, unexpected nil maps or panics converted to errors in OIDC, registration, or key endpoints.
Common situations: Corrupted or locked SQLite database file (disk full, permissions); PostgreSQL unreachable so a DB call returns an error that bubbles into a handler; a node/user record in a state the code did not expect; an object containing channels/funcs failing json.Marshal.
Related errors
- health check timed out
- empty auth key in response
- command aborted by user
- --name or --identifier flag is required
- HEADSCALE_CLI_API_KEY environment variable needs to be set
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/3f9d9bdfc7c090b7.
Report an issue: GitHub.