juicedata/juicefs · error

invalid type: %T

Error message

invalid type: %T

What it means

Thrown by dbMeta.getSession when the row passed to it is neither *session2 (current schema) nor *session (legacy schema). This is an internal invariant: GetSession/FindSession only ever construct those two row types, so this branch should be unreachable. It surfaces only if internal code evolves inconsistently (a new row type added without extending the switch).

Source

Thrown at pkg/meta/sql.go:836

}

func (m *dbMeta) getSession(row interface{}, detail bool) (*Session, error) {
	var s Session
	var info []byte
	switch row := row.(type) {
	case *session2:
		s.Sid = row.Sid
		s.Expire = time.Unix(row.Expire, 0)
		info = row.Info
	case *session:
		s.Sid = row.Sid
		s.Expire = time.Unix(row.Heartbeat, 0).Add(time.Minute * 5)
		info = row.Info
		if info == nil { // legacy client has no info
			info = []byte("{}")
		}
	default:
		return nil, fmt.Errorf("invalid type: %T", row)
	}
	if err := json.Unmarshal(info, &s); err != nil {
		return nil, fmt.Errorf("corrupted session info; json error: %s", err)
	}
	if detail {
		var (
			srows []sustained
			frows []flock
			prows []plock
		)
		err := m.roTxn(Background(), func(ses *xorm.Session) error {
			if err := ses.Find(&srows, &sustained{Sid: s.Sid}); err != nil {
				return fmt.Errorf("find sustained %d: %s", s.Sid, err)
			}
			s.Sustained = make([]Ino, 0, len(srows))
			for _, srow := range srows {
				s.Sustained = append(s.Sustained, srow.Inode)
			}

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Inspect the %T in the message to find which row type leaked into getSession.
  2. Add a case for that row type in the switch in getSession, mapping Sid/Expire/Info.
  3. If seen in stock JuiceFS, file a bug — it indicates an internal code path inconsistency.

Example fix

// before (internal code passes an unmapped row type)
return nil, fmt.Errorf("invalid type: %T", row)
// after: extend the switch
switch row := row.(type) {
case *session2: ...
case *session: ...
case *myNewSessionRow:
    s.Sid = row.Sid
    s.Expire = time.Unix(row.Expire, 0)
    info = row.Info
}
Defensive patterns

Strategy: type-guard

Validate before calling

// assert the row type before calling internal getSession
switch row.(type) {
case *meta.Session2, *meta.LegacySession:
    // ok
default:
    log.Fatalf("unsupported session row type %T", row)
}

Type guard

func validSessionRow(row interface{}) bool {
    switch row.(type) {
    case *session2, *session:
        return true
    }
    return false
}

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "invalid type: ") {
        logger.Fatalf("internal bug: unmapped session row type — report to maintainers")
    }
}

Prevention

When it happens

Trigger: A developer calls getSession with a row type other than *session2 or *session — practically only possible when modifying pkg/meta/sql.go and adding a new session table/row type without updating the switch. Not triggerable by normal API users.

Common situations: During development of a new metadata schema variant; during a custom fork or patch of the SQL engine; never in production for stock JuiceFS.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/7a36bfa2a619e731. Report an issue: GitHub.