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

  1. 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.
  2. Check database health: SQLite file permissions/disk space, or PostgreSQL connectivity and migrations being up to date.
  3. 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.
  4. 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

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


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/3f9d9bdfc7c090b7. Report an issue: GitHub.