ory/kratos · error

failed to unmarshal webhook configuration for

Error message

failed to unmarshal webhook configuration for %s: %w

What it means

getHooks builds the typed hook list for a credential lifecycle event. When a webhook hook's JSON config cannot be unmarshalled into request.Config, it logs the raw config and returns this wrapped error, aborting construction of the whole hook chain for that credentials type. The message names the credentials type and the underlying JSON error.

Solutions

  1. Validate the hook's "config" JSON parses and matches request.Config (string "url", optional "method", "body", "auth" object with string fields)
  2. Validate all JSON config files/DB rows with jq or a JSON linter before deploying
  3. Check the logged raw_config field in the error log to see the exact offending bytes
  4. Enable the hook via a config snippet known-good from the repo's test fixtures

Example fix

// before
{"hooks": [{"job": "web_hook", "config": {"url": http://x, "method": "POST"}}]}
// after
{"hooks": [{"job": "web_hook", "config": {"url": "http://x", "method": "POST"}}]}
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate webhook hook config before registering
func validWebHookConfig(raw json.RawMessage) error {
	var cfg request.Config
	if err := json.Unmarshal(raw, &cfg); err != nil {
		return fmt.Errorf("invalid web_hook config: %w", err)
	}
	return nil
}

Type guard

func isWebHookConfigOK(raw json.RawMessage) bool {
	var cfg request.Config
	return len(raw) > 0 && json.Unmarshal(raw, &cfg) == nil
}

Try / catch

hooks, err := reg.PostRegistrationPostPersistHooks(ctx, credentialsType)
if err != nil {
	var jsonErr *json.UnmarshalTypeError
	if errors.As(err, &jsonErr) {
		log.Printf("fix web_hook config: field %s at offset %d", jsonErr.Field, jsonErr.Offset)
	}
	return err
}

Prevention

When it happens

Trigger: A hook entry with key "web_hook" has a Config field that is not valid JSON for request.Config (e.g. malformed JSON, wrong top-level shape like an array or string, or a field with an incompatible type such as "url": 123).

Common situations: Typo in JSON config (trailing comma, single quotes); copying a YAML config into a JSON field; supplying the webhook URL as a number or object where a string is expected; renaming config keys after an Ory Kratos upgrade.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07). Data as JSON: /api/errors/a846be773c8b4e7d. Report an issue: GitHub.

Appendix: source

Thrown at driver/registry_default_hooks.go:88

func getHooks[T any](m *RegistryDefault, credentialsType string, configs []config.SelfServiceHook) ([]T, error) {
	hooks := make([]T, 0, len(configs))

	var addSessionIssuer bool
allHooksLoop:
	for _, hookConfig := range configs {
		switch hookConfig.Name {
		case hook.KeySessionIssuer:
			// The session issuer hook always needs to come last.
			addSessionIssuer = true
		case hook.KeySessionDestroyer:
			if h, ok := any(m.HookSessionDestroyer()).(T); ok {
				hooks = append(hooks, h)
			}
		case hook.KeyWebHook:
			cfg := request.Config{}
			if err := json.Unmarshal(hookConfig.Config, &cfg); err != nil {
				m.l.WithError(err).WithField("raw_config", string(hookConfig.Config)).Error("failed to unmarshal hook configuration, ignoring hook")
				return nil, errors.WithStack(fmt.Errorf("failed to unmarshal webhook configuration for %s: %w", credentialsType, err))
			}
			if h, ok := any(hook.NewWebHook(m, &cfg)).(T); ok {
				hooks = append(hooks, h)
			}
		case hook.KeyRequireVerifiedAddress:
			if h, ok := any(m.HookAddressVerifier()).(T); ok {
				hooks = append(hooks, h)
			}
		case hook.KeyVerificationUI:
			if h, ok := any(m.HookShowVerificationUI()).(T); ok {
				hooks = append(hooks, h)
			}
		case hook.KeyVerifier:
			if h, ok := any(m.HookVerifier()).(T); ok {
				hooks = append(hooks, h)
			}
		case hook.KeyVerifyNewAddress:
			if h, ok := any(m.HookVerifyNewAddress()).(T); ok {

View on GitHub (pinned to b86338da04)