The Bug:
When the player is given an item on the same tick that it is placed on, a desync occurs and a visual discrepency can be seen. With an item stack greater of one, the player is given a "ghost item" (an item that exists, but is not visible to the player), and with a stack greater than one the game simply does not update the count.
Steps to Reproduce:
(Must be in survival)
Place down a command block, and enter the following command:
/give @p redstone_block
Place one or more redstone block(s) next to the command block to activate it
Observed Results:
The player will properly place down the redstone block, and be given an item, but this will not be properly displayed to the player. Instead, the redstone block will become a "ghost item" that the player cannot see inside their inventory unless they interact with the slot (via placement, moving the item, or dropping it).
Expected Results:
The player would be given the redstone block by the command, and this would properly display to the player showing an accurate item stack count.
Screenshots/Videos:
Recreating this issue with an item stack of 1:
[media]Recreating this issue with an item stack greater than 1:
[media]Code Analysis / Suggested Fix (MCP 1.20.1):
One possible solution I have been able to find, is to update the players inventory after placing a block. This can be done inside the place() method of BlockItem (net.minecraft.world.item) after checking to see whether or not the player is in creative, and after shrinking the item stack if in survival.
public InteractionResult place(BlockPlaceContext blockPlaceContext) {
if (!this.getBlock().isEnabled(blockPlaceContext.getLevel().enabledFeatures())) {
return InteractionResult.FAIL;
} else if (!blockPlaceContext.canPlace()) {
return InteractionResult.FAIL;
} else {
BlockPlaceContext blockplacecontext = this.updatePlacementContext(blockPlaceContext);
if (blockplacecontext == null) {
return InteractionResult.FAIL;
} else {
BlockState blockstate = this.getPlacementState(blockplacecontext);
if (blockstate == null) {
return InteractionResult.FAIL;
} else if (!this.placeBlock(blockplacecontext, blockstate)) {
return InteractionResult.FAIL;
} else {
BlockPos blockpos = blockplacecontext.getClickedPos();
Level level = blockplacecontext.getLevel();
Player player = blockplacecontext.getPlayer();
ItemStack itemstack = blockplacecontext.getItemInHand();
BlockState blockstate1 = level.getBlockState(blockpos);
if (blockstate1.is(blockstate.getBlock())) {
blockstate1 = this.updateBlockStateFromTag(blockpos, level, itemstack, blockstate1);
this.updateCustomBlockEntityTag(blockpos, level, player, itemstack, blockstate1);
blockstate1.getBlock().setPlacedBy(level, blockpos, blockstate1, player, itemstack);
if (player instanceof ServerPlayer) {
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, blockpos, itemstack);
}
}
SoundType soundtype = blockstate1.getSoundType();
level.playSound(player, blockpos, this.getPlaceSound(blockstate1), SoundSource.BLOCKS, (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F);
level.gameEvent(GameEvent.BLOCK_PLACE, blockpos, GameEvent.Context.of(player, blockstate1));
if (player == null || !player.getAbilities().instabuild) {
itemstack.shrink(1);
-> FIX START
player.containerMenu.broadcastChanges();
-> FIX END
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
}
}
}
This fix provides the following behavior:
[media]
Notes:
As Kieren Chantrell has pointed out in their comment, this also occurs with functions as well.
Linked issues
duplicates 1
Attachments
Comments 3
Requesting ownership of this issue to provide more information, and maintain the issue.

Perhaps this should be forward-resolved to MC-239935, as that describes the same issue but has been triaged by Mojang.
Can confirm this occurs with functions also. Running the following in a loop and throwing a stone block on the ground causes the same thing to happen.
execute as @e[type=item,nbt={Item:{id:"minecraft:stone"}}] run give @p minecraft:stone
execute as @e[type=item,nbt={Item:{id:"minecraft:stone"}}] run kill @s