Skip to content

T19-01 Published-post editor schema compatibility and crash containment #550

Closed
5 of 5 issues completed

Description

@edithatogo

Problem

A published post created/updated through the CLI can render normally and remain valid in Substack's primary rich-text editor, yet make the browser edit screen unstable or crash.

Verified reproduction: https://rareinsights.substack.com/p/death-by-donation-a-repugnant-transaction (post ID 210551946). The public page is healthy, but the authenticated published-post editor logs:

Editor content error encountered: Error: [tiptap error]: Invalid JSON content
RangeError: Unknown node type: image

Differential diagnosis

Confirmed

The published-post edit screen mounts two visible Tiptap editors:

  1. The primary editor accepts the complete stored post JSON without an error.
  2. A secondary restricted editor exposes only doc, paragraph, text, and hardBreak, with basic link/bold/italic marks, but receives the full rich body.

The secondary editor rejects rich nodes present in the post:

  • image (first observed failure)
  • heading
  • blockquote
  • orderedList / bulletList via listItem

It also renders a second, very long document tree beside the primary editor. In the reproduction, the primary document was approximately 15,434 px tall and the secondary copy approximately 11,170 px tall. The schema exception plus duplicated DOM is the leading explanation for browser-tab instability.

Ruled out for this post

  • corrupted or malformed ProseMirror JSON
  • invalid content in Substack's full editor schema
  • excessive individual string/node size
  • duplicate node IDs
  • broken image URLs or failed asset requests
  • repository-relative reader links
  • public-page HTTP, console, or page errors

The body is about 51.5 KB / 536 JSON objects and all three images load.

Related but distinct compatibility defect

A scheduled control post containing a table fails in the primary editor with:

RangeError: Unknown node type: tableHeader

This is a genuine CLI/editor schema-compatibility problem and should not be conflated with the published-post secondary-editor defect.

Remaining uncertainty

The secondary editor's intended Substack function is not established. This may be a Substack regression, an undocumented published-revision representation constraint, or a post shape that triggers an otherwise latent editor bug. The CLI should therefore diagnose and contain the failure without claiming ownership of Substack's UI internals.

Detection options

A. Static capability profiles (recommended baseline)

Validate generated nodes and marks against versioned profiles:

  • full draft editor profile
  • restricted auxiliary editor profile
  • published-revision profile

Report the first incompatible node with its JSON path and all incompatible-node counts.

Trade-off: deterministic and fast, but profiles can drift when Substack changes its private editor schema.

B. Authenticated disposable-editor canary

Create or update a non-public canary, open the authenticated edit route, capture Tiptap errors, mounted editor schemas, DOM duplication, and page stability, then delete/restore the canary.

Trade-off: detects upstream drift but is slower, authentication-dependent, potentially rate-limited, and must never perform unconfirmed publication.

C. Stored-post round-trip inspection

After create/update, retrieve the post and compare draft_body, published body, metadata, and node inventory; classify published-revision risk before reporting success.

Trade-off: catches transport differences but cannot alone prove the browser editor will mount safely.

D. Payload complexity guardrails

Record body bytes, node count/depth, image/list/table counts, and estimated duplicated DOM cost.

Trade-off: useful diagnostics, not a substitute for schema validation; thresholds risk false positives.

Resolution options

1. Fail closed on known incompatible nodes for editable published revisions

Reject or require explicit override when a target profile cannot represent the body.

Benefit: safest and transparent.
Cost: blocks otherwise publishable rich posts when only Substack's auxiliary editor is defective.

2. Normalize only unsupported structures

Examples: convert tables to accessible Markdown/HTML-safe lists; preserve headings/images for the full editor; produce a paragraph-safe auxiliary representation only if the API has a distinct supported field.

Benefit: retains most formatting.
Cost: lossy and unsafe unless the secondary representation is an actual supported API concept.

3. Editor-native round trip

Serialize through the exact schema discovered from the authenticated primary editor, then verify the resulting published-revision screen.

Benefit: strongest compatibility signal.
Cost: coupled to private implementation details and still may not control the secondary editor.

4. CLI-only update fallback

When the browser editor is known unsafe, permit update-in-place through the CLI and emit a durable warning/receipt.

Benefit: immediate operational continuity.
Cost: workaround only; users remain unable to use Substack's browser editor.

5. Upstream escalation with minimal reproducer

Provide Substack a privacy-safe fixture containing one heading, image, blockquote, and list that the full editor accepts but the secondary editor rejects.

Benefit: targets the actual UI defect.
Cost: external resolution and timing are outside this project's control.

Recommendation

Implement layered protection:

  1. Add static full/restricted/published-revision schema profiles and precise JSON-path diagnostics.
  2. Treat tables unsupported by the primary editor as a hard error unless explicitly normalized.
  3. Treat secondary-editor incompatibility as a prominent published-editability risk, not silent success.
  4. Add an authenticated disposable canary to the compatibility matrix, with rate-limit, CAPTCHA/2FA, cleanup, and no-publication boundaries.
  5. Preserve CLI update-in-place as a documented contingency.
  6. Produce a minimal upstream reproduction and avoid destructive rewriting until Substack's secondary editor contract is understood.

Acceptance criteria

  • A fixture containing images/headings/lists/blockquote is valid for the full profile and reports exact incompatibilities for the restricted profile.
  • A table fixture reports tableHeader incompatibility before any live write.
  • Create/update receipts distinguish public rendering, primary-editor validity, secondary-editor/editability risk, and live browser verification.
  • Authenticated canary tests never publish or email without explicit confirmation and clean up deterministically.
  • Rate limits, auth expiry, CAPTCHA/2FA, upstream schema drift, and cleanup failure have explicit stop conditions.
  • Documentation describes CLI-only update as a contingency, not a fix.
  • A privacy-safe upstream reproduction fixture is generated.

Scope boundary

Planning and diagnostics must not silently modify published content, strip rich structures, or claim Substack UI compatibility without an authenticated verification receipt.

Activity

  1. edithatogo commented on Aug 11, 2026

    @edithatogo
    OwnerAuthor

    Additional live evidence from 2026-08-11:

    • draft create correctly resolved the existing published mapping (210551946) and built an update-in-place plan preserving slug and section.
    • The API workflow stopped before writing because /api/v1/handle/options no longer matched the expected shape (status: schema-drift, browser fallback required). This is a separate transport-preflight failure and should be covered by contingencies in this issue.
    • The authenticated browser edit route reproduced the user-facing alert verbatim: “Something has gone wrong. Please refresh the page and try again.” The console again recorded Editor content error encountered: [tiptap error]: Invalid JSON content.
    • After dismissing the alert, the primary editor remained usable. A minimal text-only PUT to the existing draft succeeded (HTTP 200), and the normal Update → Update now UI successfully revised the live post without changing its slug, section, headings, lists or images.
    • Reloading the published page confirmed the revision. Reloading the editor still raises the same alert, so the manual flow is a contingency, not a fix.

    This further supports keeping four states separate: API preflight compatibility, stored-draft update success, public revision success, and browser edit-screen compatibility.

  2. changed the title [-]Detect and prevent Substack published-post editor schema crashes[/-] [+]T19-01 Published-post editor schema compatibility and crash containment[/+] on Aug 11, 2026
  3. added
    conductorOwned by a Conductor track or task
    conductor-trackConductor programme track issue
    bugSomething isn't working
    on Aug 11, 2026
  4. edithatogo commented on Aug 12, 2026

    @edithatogo
    OwnerAuthor

    Season 1 incremental audit — 2026-08-12

    The eight-post live audit confirms the existing differential diagnosis and adds a complete bounded corpus.

    Target Draft ID Stored body Browser editor Incompatible nodes
    Pilot 210551946 51,771 bytes, valid JSON Fails with Invalid JSON content rich-content/published-editor path; first known failure is image
    Season 1 cover 210552674 5,722 bytes, valid JSON Passes, editable and scheduled none
    ME-001 210552872 46,593 bytes, valid JSON Fails tableHeader (7)
    ME-002 210552983 46,028 bytes, valid JSON Fails; appears empty tableHeader (7)
    ME-003 210553075 33,513 bytes, valid JSON Fails tableHeader (4)
    ME-004 210553156 34,939 bytes, valid JSON Fails tableHeader (4)
    ME-005 210553256 45,506 bytes, valid JSON Fails tableHeader (8)
    ME-006 210553389 53,890 bytes, valid JSON Fails tableHeader (11)

    Directly reproduced console signatures include:

    Editor content error encountered: Error: [tiptap error]: Invalid JSON content
    RangeError: Unknown node type: tableHeader
    

    The ME-002 body is not empty or lost: authenticated GET /api/v1/drafts/210552983 returns HTTP 200 and a 46,028-byte parseable draft_body. The editor fails while deserializing it.

    Updated differential diagnosis

    1. Primary-editor table incompatibility — confirmed across six scheduled essays. The CLI parser/transport accepts and writes table nodes including tableHeader, but the current Substack editor schema rejects that node.
    2. Published pilot incompatibility — separate failure mode. The pilot has no tables. Its rich body triggers the published-editor failure already documented in the track issue.
    3. Not general scheduling, auth, or body loss. All eight draft-detail endpoints return 200 and valid JSON; the paragraph-only Season 1 cover opens normally in the same authenticated session.
    4. Not a one-off. Seven of eight Season 1 surfaces fail; the sole pass is the structurally simple cover.

    Immediate operational containment

    For the six numbered essays, normalize tables into accessible labelled bullet lists, update the same draft IDs in place, preserve schedules and metadata, then verify both the authenticated detail endpoint and browser editor. Do not change CLI runtime for this remediation.

    The pilot requires its separate rich/published-editor containment route and should not be “fixed” by globally stripping images, headings or lists.

    Recommendation

    Keep the existing layered plan. Elevate primary-editor tableHeader detection to a hard pre-write failure; add exact JSON paths and counts to receipts; test the authenticated editor after update; retain selective normalization as an explicit, auditable contingency. Public-page success and HTTP-200 draft retrieval must not count as browser-editability success.

  5. edithatogo commented on Aug 12, 2026

    @edithatogo
    OwnerAuthor

    Season 1 operational remediation outcome — 2026-08-12

    The six numbered essays were normalized in place from table nodes to accessible labelled lists. Authenticated draft-detail verification shows:

    • 11 tables and 41 tableHeader nodes before remediation;
    • zero table and zero tableHeader nodes afterward;
    • all six original draft IDs retained;
    • all six scheduled timestamps retained exactly;
    • all six bodies remain non-empty (32,332–50,099 bytes).

    The CLI runtime was not changed.

    Important residual finding

    ME-001 still raises Substack's generic Tiptap Invalid JSON content alert after all table nodes are removed. Its remaining rich shape includes an inline image, headings and list nodes. This aligns the scheduled-post corpus with the separate published-pilot/auxiliary-editor failure already documented in #550.

    Therefore the Season 1 corpus exposes two stacked compatibility failures:

    1. a CLI-preventable primary-editor tableHeader incompatibility, now operationally removed from all six essays; and
    2. a broader Substack rich-content/auxiliary-editor incompatibility that remains when the user retains the requested images and document structure.

    Flattening all rich nodes to paragraphs would probably restore browser editability, but would remove inline illustrations, headings, semantic lists and blockquotes. That is a destructive publication-design trade-off, not a safe automatic repair.

    Recommendation

  6. edithatogo commented on Sep 6, 2026

    @edithatogo
    OwnerAuthor

    Completed via PR #601. Track T19-01 published-post editor schema compatibility and crash containment implemented across all 5 phases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingconductorOwned by a Conductor track or taskconductor-trackConductor programme track issue

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions