knadh/listmonk · error

could not not decode type %T -> %T

Error message

could not not decode type %T -> %T

What it means

StringIntMap implements sql.Scanner; Scan expects nil or []byte (JSONB from Postgres). If the driver delivers any other Go type, json.Unmarshal can't be attempted and this (typo'd) error is returned describing the source and target types.

Source

Thrown at models/common.go:133

// StringIntMap is used to define DB Scan()s.
type StringIntMap map[string]int

// Value returns the JSON marshalled SubscriberAttribs.
func (s JSON) Value() (driver.Value, error) {
	return json.Marshal(s)
}

// Scan unmarshals JSONB from the DB.
func (s JSON) Scan(b any) error {
	if b == nil {
		s = make(JSON)
		return nil
	}

	if data, ok := b.([]byte); ok {
		return json.Unmarshal(data, &s)
	}
	return fmt.Errorf("could not not decode type %T -> %T", b, s)
}

// Scan unmarshals JSONB from the DB.
func (s StringIntMap) Scan(src any) error {
	if src == nil {
		s = make(StringIntMap)
		return nil
	}

	if data, ok := src.([]byte); ok {
		return json.Unmarshal(data, &s)
	}
	return fmt.Errorf("could not not decode type %T -> %T", src, s)
}

// Scan implements the sql.Scanner interface.
func (h *Headers) Scan(src any) error {
	var b []byte

View on GitHub (pinned to 670c01717d)

Solutions

  1. Add a string case: if data, ok := b.(string); ok { return json.Unmarshal([]byte(data), &s) }
  2. Ensure the query doesn't cast the JSONB column to text; select it as-is so the driver returns []byte
  3. Pass []byte(...) when calling Scan directly in tests/code

Example fix

// before
if data, ok := b.([]byte); ok {
    return json.Unmarshal(data, &s)
}
// after
switch data := b.(type) {
case []byte:
    return json.Unmarshal(data, &s)
case string:
    return json.Unmarshal([]byte(data), &s)
}
Defensive patterns

Strategy: type-guard

Validate before calling

switch v := value.(type) {
case nil, []byte:
    // OK to pass to Scan
case string:
    value = []byte(v)
default:
    return fmt.Errorf("cannot scan %T into StringIntMap", value)
}

Type guard

func isScannableJSONB(v any) bool {
    switch v.(type) {
    case nil, []byte, string:
        return true
    }
    return false
}

Try / catch

if err := m.Scan(dbValue); err != nil {
    if strings.Contains(err.Error(), "could not not decode") {
        // coerce dbValue to []byte (if string) and retry once
    }
}

Prevention

When it happens

Trigger: Database/driver returns a value that is neither nil nor []byte — e.g. a string instead of []byte — when scanning a JSONB column into StringIntMap.

Common situations: Using a driver or scan helper that decodes JSONB into string instead of []byte; hand-written queries casting the column (e.g. ::text); unit tests passing string values to Scan.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/3a61fc5db173b8d2. Report an issue: GitHub.