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
- Validate the hook's "config" JSON parses and matches request.Config (string "url", optional "method", "body", "auth" object with string fields)
- Validate all JSON config files/DB rows with jq or a JSON linter before deploying
- Check the logged raw_config field in the error log to see the exact offending bytes
- 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
- Validate the whole Kratos config JSON against the schema at CI time
- Run jq . over every JSON config block before applying changes
- Keep webhook configs in linted config files, not hand-edited DB rows
- Test hook registration in a staging environment before production rollout
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
- failed to unmarshal notify_previous_addresses configuration…
- api_key auth strategy requires a string name
- unexpected node type
- boolean unmarshal error: invalid input
- no credentials found
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)