Sanity Pioneers: Get early access to betas, extra AI credits, and a direct line to the engineering team. Apply now

it seems like there are some ghost mark types creeping their way into my block content

9 repliesLast updated: Nov 30, 2025

anyone have any idea how to get rid of these warnings? it seems like there are some ghost mark types creeping their way into my block content
Unknown mark type "a5a909c5150a", please specify a serializer for it in the `serializers.marks` prop
Unknown mark type "80061e4e5eb1", please specify a serializer for it in the `serializers.marks` prop
Unknown mark type "1df06a28145f", please specify a serializer for it in the `serializers.marks` prop
(looks like this warning comes from block-content-to-hyperscript here
https://github.com/sanity-io/block-content-to-hyperscript/blob/c8c18eb3adc3ca67daba80ad822a2eb21a2f6a3c/src/serializers.js#L31 )
AI Update

Good news - these "ghost marks" with hex IDs are a common issue with leftover annotation references in your Portable Text content! Here's what's happening and how to fix it.

The Problem

When you add an annotation (like a link) to text in Portable Text, Sanity creates two things:

When you remove the annotation through the editor, sometimes the mark reference gets left behind in the text span even though the markDefs entry is gone. This creates "ghost marks" that your serializer doesn't recognize.

The Solution: Clean Your Content

You need to clean up these orphaned mark references from your documents. Here's a migration script approach using the Sanity migration toolkit:

import {defineMigration, at} from 'sanity/migrate'

const cleanGhostMarks = defineMigration({
  title: 'Clean ghost marks from portable text',
  documentTypes: ['yourDocType'],
  
  migrate: {
    document(doc, context) {
      // Clean your block content field
      if (doc.yourBlockField) {
        return at('yourBlockField', cleanBlockContent(doc.yourBlockField))
      }
    }
  }
})

function cleanBlockContent(blocks) {
  return blocks.map(block => {
    if (block._type !== 'block' || !block.children) return block
    
    const validMarkKeys = new Set(
      (block.markDefs || []).map(def => def._key)
    )
    
    return {
      ...block,
      children: block.children.map(child => ({
        ...child,
        marks: (child.marks || []).filter(mark => 
          validMarkKeys.has(mark) || 
          ['strong', 'em', 'underline', 'code'].includes(mark) // keep decorators
        )
      }))
    }
  })
}

This script filters out any marks that don't have a corresponding entry in markDefs, keeping only valid decorator marks (bold, italic, etc.) and annotations.

Quick Fix: Add Default Serializers

While you work on cleaning the data, you can suppress the warnings by adding a catch-all serializer:

// For @sanity/block-content-to-react (deprecated)
<BlockContent
  blocks={content}
  serializers={{
    marks: {
      // Your existing serializers...
      // These hex IDs will just render as plain text
      'a5a909c5150a': ({children}) => children,
      '80061e4e5eb1': ({children}) => children,
      '1df06a28145f': ({children}) => children,
    }
  }}
/>

// For @portabletext/react (recommended)
<PortableText
  value={content}
  components={{
    marks: {
      // Add serializers for each ghost mark
      'a5a909c5150a': ({children}) => <>{children}</>,
      '80061e4e5eb1': ({children}) => <>{children}</>,
      '1df06a28145f': ({children}) => <>{children}</>,
    }
  }}
/>

Note: If you're still using @sanity/block-content-to-react, consider migrating to @portabletext/react as the old library is deprecated.

Prevention

To prevent this in the future:

The root cause is usually from older versions of the editor or custom annotation handling code that didn't properly clean up both parts of the annotation.

Show original thread
9 replies

Was this answer helpful?

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Related contributions