ory/kratos · error

webhook response could not be unmarshalled properly from…

Error message

webhook response could not be unmarshalled properly from JSON

What it means

The webhook responded with 200 and its body was read successfully, but json.Unmarshal could not decode it into the expected {identity: ...} structure. The upstream webhook returned a body that is not valid JSON for the contract kratos expects on success responses.

Solutions

  1. Make the webhook return valid JSON, or an empty body with a non-200 status if it has nothing to modify
  2. Ensure a 200 response wraps any identity modifications in an {"identity": {...}} envelope
  3. Check for HTML error pages or plain-text bodies being served instead of JSON
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at selfservice/hook/web_hook.go:471 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at selfservice/hook/web_hook.go:471

}

func parseWebhookResponse(resp *http.Response, id *identity.Identity) (err error) {
	if resp == nil {
		return errors.Errorf("empty response provided from the webhook")
	}

	if resp.StatusCode == http.StatusOK {
		type localIdentity identity.Identity
		var hookResponse struct {
			Identity *localIdentity `json:"identity"`
		}
		// io.ReadAll is safe, because resp.Body is already a limited reader.
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return errors.Wrap(err, "webhook response body could not be read")
		}
		if err = json.Unmarshal(body, &hookResponse); err != nil {
			return errors.Wrap(err, "webhook response could not be unmarshalled properly from JSON")
		}

		if hookResponse.Identity == nil {
			return nil
		}

		if len(hookResponse.Identity.Traits) > 0 {
			id.Traits = hookResponse.Identity.Traits
		}

		if len(hookResponse.Identity.SchemaID) > 0 {
			id.SchemaID = hookResponse.Identity.SchemaID
		}

		if len(hookResponse.Identity.State) > 0 {
			id.State = hookResponse.Identity.State
		}

View on GitHub (pinned to b86338da04)