mattermost-community/focalboard · error

error validating block %s: %w

Error message

error validating block %s: %w

What it means

This is a wrapped validation failure raised by insertBlock when the incoming model.Block fails block.IsValid(). It means the block (card, board item, text, etc.) violates Focalboard's structural rules (e.g. empty ID, invalid type, bad parent/child fields). The original validation error is preserved via %w so callers can inspect it with errors.As/Is against the model's ErrInvalidBlock error chain.

Source

Thrown at server/services/store/sqlstore/blocks.go:232

		}

		err = json.Unmarshal([]byte(fieldsJSON), &block.Fields)
		if err != nil {
			// handle this error
			s.logger.Error(`ERROR blocksFromRows fields`, mlog.Err(err))

			return nil, err
		}

		results = append(results, &block)
	}

	return results, nil
}

func (s *SQLStore) insertBlock(db sq.BaseRunner, block *model.Block, userID string) error {
	if err := block.IsValid(); err != nil {
		return fmt.Errorf("error validating block %s: %w", block.ID, err)
	}

	fieldsJSON, err := json.Marshal(block.Fields)
	if err != nil {
		return err
	}

	existingBlock, err := s.getBlock(db, block.ID)
	if err != nil && !model.IsErrNotFound(err) {
		return err
	}

	block.UpdateAt = utils.GetMillis()
	block.ModifiedBy = userID

	insertQuery := s.getQueryBuilder(db).Insert("").
		Columns(
			"channel_id",

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Log/inspect the wrapped inner error (errors.Unwrap or %v of the returned error) to see exactly which field IsValid rejected
  2. Ensure block.ID is set to a valid generated ID (e.g. utils.NewID()) before inserting
  3. Ensure block.Type is one of the supported model.BlockType values and other required fields (BoardID, ParentID, Title) are populated
  4. If the data comes from an import, pre-validate every block with block.IsValid() before calling the store

Example fix

// before
block := &model.Block{Title: "hello"}
s.InsertBlock(block, userID)
// after
block := &model.Block{ID: utils.NewID(), Type: model.TypeCard, Title: "hello", BoardID: boardID, ParentID: parentID}
if err := block.IsValid(); err != nil {
    return err
}
s.InsertBlock(block, userID)
Defensive patterns

Strategy: validation

Validate before calling

func validForInsert(b *model.Block) error {
    if b == nil || b.ID == "" {
        return fmt.Errorf("block ID required")
    }
    return b.IsValid()
}
// call before: if err := validForInsert(block); err != nil { return err }

Type guard

func isInsertableBlock(b *model.Block) bool {
    return b != nil && b.ID != "" && b.IsValid() == nil
}

Try / catch

if err := store.InsertBlock(block, userID); err != nil {
    var verr error
    if errors.As(err, &verr) && strings.Contains(err.Error(), "error validating block") {
        return fmt.Errorf("block %s invalid: %w", block.ID, err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling InsertBlock or InsertBlocks (or the higher-level patchBlock/createBoardsAndBlocks flows) with a block whose ID is empty, whose Type is not a registered block type, whose boardID/parentID references are malformed, or otherwise failing model.Block.IsValid().

Common situations: Importing JSON block dumps with missing or empty 'id' fields; writing client code that constructs blocks with a Type typo; API automation sending blocks without required fields; restoring backups with legacy/corrupted block shapes.

Related errors


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