Automate the Boring Stuff – Chess Dictionary Validator – Optimal Solution
As you may know already, your validation rules are both incomplete and too strict. For example, a black bishop must be on a black square, and there can be more-than-expected numbers of the pieces after a pawn is advanced and promoted (usually, but not always, to a queen). I'll ignore those issues and focus on the code.
Get the so-called magic strings and numbers out of the code and into named
constants and/or data structures. Here's a draft of some constants that might
be handy for your task. As you add more validation checks, you might need to
augment or adjust these.
import sys
from collections import namedtuple

BLACK = 'b'
WHITE = 'w'

KING = 'king'
QUEEN = 'queen'
ROOK = 'rook'
BISHOP = 'bishop'
KNIGHT = 'knight'
PAWN = 'pawn'

COLORS = {BLACK, WHITE}
PIECES = {KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN}

RANKS = set('12345678')
COLUMNS = set('abcdefgh')

VALID_COUNTS = {
    PAWN:   range(0, 9),
    KNIGHT: range(0, 3),
    BISHOP: range(0, 3),
    ROOK:   range(0, 3),
    QUEEN:  range(0, 2),
    KNIGHT: range(1, 2),
}

Set up a simple data structure to facilitate testing and debugging
as you write the script. For example, we have the board you gave us
and an invalid board I added.
INPUT_BOARDS = {
    'orig': {
        '1h': 'bking',
        '6c': 'wqueen',
        '2g': 'bbishop',
        '5h': 'bqueen',
        '3e': 'wking',
    },
    'bad1': {
        '9h': 'bking',
        '9x': 'wking',
    },
}

The input format for a board is not convenient for validation because it
glues together rank-plus-column and color-plus-piece. Do parsing first, validation
second. There are various ways to arrange the parsing, but an easy, low-tech
way is with a namedtuple -- an immutable container that behaves like a tuple
but also allows one to access the attributes via their names.
By parsing the input board immediately, you can simplify the rest of the code.
The validation function should take and return data (e.g. the first
error or, even better, all errors). It should not print.
Print only in the simple, outer-shell of the program (e.g. a main() function), not it
its more complex, algorithmic center (where you are doing validation).
ParsedCell = namedtuple('ParsedCell', 'cell color_piece rank column color piece')

def main(args):
    board = parse_input_board(INPUT_BOARDS[args[0]])
    errors = check_board(board)
    if errors:
        for e in errors:
            print(e)
    else:
        print('OK')

def parse_input_board(input_board):
    return tuple(
        ParsedCell(
            cell,
            color_piece,
            cell[0:1],       # A safe technique even if cell is an empty string.
            cell[1:],
            color_piece[0:1],
            color_piece[1:],
        )
        for cell, color_piece in input_board.items()
    )

Now sitting on a more solid foundation, the program's validation
code becomes (1) simpler to write and (2) easier to read
because it is more declarative or self-documenting.
def check_board(board):
    errors = []
    for pcell in board:
        if pcell.rank not in RANKS:
            msg = emsg('Invalid rank', pcell.cell)
            errors.append(msg)

        if pcell.color not in COLORS:
            msg = emsg('Invalid color', pcell.cell)
            errors.append(msg)

        # Etc.

    return errors

def emsg(msg, item):
    return f'{msg}: {item}'

if __name__ == '__main__':
    main(sys.argv[1:])