mattermost-community/focalboard · error

cannot parse datetime '%s' for board_members_history scan: %

Error message

cannot parse datetime '%s' for board_members_history scan: %w

What it means

boardMemberHistoryEntriesFromRows parses the insert_at column of board_members_history rows into time.Time using a dialect-specific layout; when time.Parse fails on the string value, this error wraps the unparseable value and the underlying error. It means the database returned insert_at in an unexpected textual format.

Source

Thrown at server/services/store/sqlstore/board.go:183

		err := rows.Scan(
			&boardMemberHistoryEntry.BoardID,
			&boardMemberHistoryEntry.UserID,
			&boardMemberHistoryEntry.Action,
			&insertAt,
		)
		if err != nil {
			return nil, err
		}

		// parse the insert_at timestamp which is different based on database type.
		dateTemplate := "2006-01-02T15:04:05Z0700"
		if s.dbType == model.MysqlDBType {
			dateTemplate = "2006-01-02 15:04:05.000000"
		}
		ts, err := time.Parse(dateTemplate, insertAt.String)
		if err != nil {
			return nil, fmt.Errorf("cannot parse datetime '%s' for board_members_history scan: %w", insertAt.String, err)
		}
		boardMemberHistoryEntry.InsertAt = ts

		boardMemberHistoryEntries = append(boardMemberHistoryEntries, &boardMemberHistoryEntry)
	}

	return boardMemberHistoryEntries, nil
}

func (s *SQLStore) getBoardByCondition(db sq.BaseRunner, conditions ...interface{}) (*model.Board, error) {
	boards, err := s.getBoardsByCondition(db, conditions...)
	if err != nil {
		return nil, err
	}

	return boards[0], nil
}

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Check the quoted datetime in the error to see the actual format returned and compare with the expected template
  2. Verify the MySQL driver version (e.g. go-sql-driver/mysql) returns DATETIME as '2006-01-02 15:04:05.000000'; add parseTime/loc DSN params if needed
  3. Ensure rows were written by Focalboard itself (not imported with foreign timestamp formats)
  4. If the driver can't be changed, patch the dateTemplate to match your DB's output format

Example fix

// before
dateTemplate := "2006-01-02T15:04:05Z0700"
if s.dbType == model.MysqlDBType {
    dateTemplate = "2006-01-02 15:04:05.000000"
}
// after
dateTemplate := "2006-01-02T15:04:05Z0700"
if s.dbType == model.MysqlDBType {
    dateTemplate = "2006-01-02 15:04:05.999999" // tolerate rows without fractional seconds
}
Defensive patterns

Strategy: validation

Validate before calling

func isExpectedDatetimeFormat(s string, dbType string) bool {
    layout := "2006-01-02T15:04:05Z0700"
    if dbType == "mysql" {
        layout = "2006-01-02 15:04:05.000000"
    }
    _, err := time.Parse(layout, s)
    return err == nil
}

Try / catch

entries, err := store.GetBoardMemberHistory(boardID)
if err != nil && strings.Contains(err.Error(), "cannot parse datetime") {
    log.Printf("unexpected insert_at format from DB: %v", err)
    return fmt.Errorf("driver/DB timestamp format mismatch: %w", err)
}

Prevention

When it happens

Trigger: getBoardMemberHistory scanning rows where insert_at's string doesn't match '2006-01-02 15:04:05.000000' (MySQL) or '2006-01-02T15:04:05Z0700' (Postgres/SQLite) — e.g. a driver returning a different formatting or zero-time representation.

Common situations: MySQL servers/drivers configured with different datetime precision or strict formatting; using an alternate driver that formats DATETIME differently; reading history rows written by another tool/version with a different timestamp format; locale/timezone formatting leaks from custom DB settings.

Related errors


AI-assisted analysis of mattermost-community/focalboard@a84bbb65e3 (2026-08-30). Data as JSON: /api/errors/e5bebe298275a3ab. Report an issue: GitHub.