ory/hydra · critical · ImmutableError

immutable configuration key "%s" was changed from "%v" to "%

Error message

immutable configuration key "%s" was changed from "%v" to "%v"

What it means

NewImmutableError builds an ImmutableError raised when a configuration key marked as immutable (must not change at runtime) receives a new value during a live reload. The error captures the key and both the old and new values, and its message states exactly what changed. It typically causes the process to terminate because continuing with a mutated immutable key would be unsafe (e.g. cryptographic secrets).

Source

Thrown at oryx/configx/error.go:24

import (
	"fmt"

	"github.com/pkg/errors"
)

type ImmutableError struct {
	From interface{}
	To   interface{}
	Key  string
	error
}

func NewImmutableError(key string, from, to interface{}) error {
	return &ImmutableError{
		From:  from,
		To:    to,
		Key:   key,
		error: errors.Errorf("immutable configuration key \"%s\" was changed from \"%v\" to \"%v\"", key, from, to),
	}
}

func (e *ImmutableError) Error() string {
	return fmt.Sprintf("immutable configuration key \"%s\" was changed from \"%v\" to \"%v\"", e.Key, e.From, e.To)
}

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Restart the process after changing an immutable key instead of relying on hot reload
  2. Use supported secret rotation mechanisms (e.g. hydra serve with multiple valid system secrets) rather than mutating the immutable key
  3. Update the config in a way that avoids touching immutable keys at runtime; only modify mutable keys live
  4. If it fires at startup unintentionally, check for duplicate config sources (env + file) defining the key with different values

Example fix

// before
# edit config.yaml system_secret in place while server is running
// after
$ systemctl restart hydra   # apply immutable key changes via restart
Defensive patterns

Strategy: type-guard

Validate before calling

func IsImmutableConfigError(err error) bool {
    var ie *configx.ImmutableError
    return errors.As(err, &ie)
}

Type guard

func AsImmutableError(err error) (*configx.ImmutableError, bool) {
    var ie *configx.ImmutableError
    ok := errors.As(err, &ie)
    return ie, ok
}

Try / catch

if err := serve(); err != nil {
    if ie, ok := AsImmutableError(err); ok {
        log.Fatalf("immutable config key %q changed (%v -> %v); restart required", ie.Key, ie.From, ie.To)
    }
    return err
}

Prevention

When it happens

Trigger: Changing an immutable config value (commonly secrets like the system secret, cookie keys, or database DSN flagged immutable) while the application is running and a config reload (file watch, env change) picks up the new value — reload() then calls NewImmutableError.

Common situations: Rotating secrets via config file edits on a watched file; changing the system secret in Kubernetes Secrets mounted as env/files; editing hydra config in-place instead of restarting the service.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/e8d896fcab3262b8. Report an issue: GitHub.