What happened?
VortexWriteOptions takes the file's array encoding allowlist entirely from the session's enabled editions, so an application cannot write an array encoding it registered itself. Registration and reads are unaffected.
The gate is new_array_context (vortex-file/src/writer.rs:358), built inside write_internal and reachable from no public API. with_strategy does not replace it and WriteStrategyBuilder::with_allow_encodings cannot widen it, so an encoding outside the enabled editions fails at vortex-array/src/serde.rs:196 with Array encoding app.identity not permitted by ctx.
Refusing to serialize a component outside the selected editions is intended, tested, and published in docs/specs/editions.md. The regression is the loss of the opt-out that #8855 and #8945 both documented as supported. #8946 made that opt-out unreachable while hardening the policy against replacement strategies, and #9364 later rewrote the docs to match the code. Nothing in #8946's description or review discussion mentions third-party or custom encodings.
The documented opt-out, before and after #8946
#8855 introduced editions as documentation only, ahead of any code:
You can also opt out of editions entirely to write custom or experimental encodings. Doing so is an explicit choice that gives up the standardization guarantee, only readers that know your encodings can read those files.
#8945 restated it in mechanical terms, one PR before the writer gate landed:
Lower-level sessions without an enabled-editions store opt out of editions entirely and can write custom or experimental encodings. A raw with_allow_encodings writer policy is another explicit opt-out.
#8946 made both halves false. A session with no EnabledEditions variable permits nothing rather than everything, which is why that PR had to add enable_all_registered_array_encodings to the lower-level test sessions in vortex-file, vortex-layout and vortex-parquet-variant. with_allow_encodings can only narrow, because the array context is resolved independently of it.
#9364 deleted the paragraph and replaced it with the current text:
Sessions created without the Vortex facade must register and enable their editions before writing files. The lower-level with_allow_encodings policy can further restrict array encodings, but cannot permit an encoding excluded by the selected editions.
Steps to reproduce
Details
vortex/tests/third_party_encoding.rs on claude/vortex-editions-third-party-0f81bv registers a minimal app.identity encoding on a default session and records what the writer does with it. All six cases pass as written, so the tests that assert a failure are the ones a fix must flip.
| Case |
Result |
| Already-encoded custom array, default strategy |
normalized away before serialization, write succeeds with the encoding absent |
| Already-encoded custom array, leaf-only strategy |
fails at serde.rs:196 |
| Replacement write strategy |
fails at serde.rs:196 |
| Strategy allowlist widened to the whole array registry |
fails at serde.rs:196 |
| Own edition family declared and enabled |
writes and round-trips |
Frozen core2026.08.1 widened by declare_inclusion |
writes and round-trips |
Restoring the pre-#8946 registry-derived allowlist on develop, with nothing else changed, flips custom_encoding_is_rejected_when_it_is_in_no_edition and with_allow_encodings_cannot_readmit_a_third_party_encoding to succeeding writes. That isolates new_array_context as the cause.
Environment
develop as of 42a5d3b.
Additional context
The workaround is for the application to declare and enable its own edition family, the way vortex-spatial and vortex-json do. That path works and independent families coexist without coordination, but it is documented nowhere an application author will look. docs/specs/editions.md presents families as a first-party mechanism, docs/developer-guide/extending/writing-an-encoding.md is a two-line stub, and docs/developer-guide/language-bindings.md still advertises Tier 3 plugins with Rust already at Tier 3 and no mention of editions.
Proposal: add an explicit per-write opt-in on VortexWriteOptions, for example with_additional_encodings(HashSet<ArrayId>), unioned into both the strategy allowlist and new_array_context. That keeps #8946's actual goal, since a replacement strategy still cannot widen anything on its own. If the project would rather not reopen the opt-out, the fix is documentation only, and the requirement belongs where plugin authors hit it.
Three related defects turned up while tracing this, each worth a separate fix:
declare_inclusion has no freeze check, so an application can add its encoding to frozen core2026.08.1 and write a file that presents as an edition whose published minimum reader is 0.84.0 but that no released reader can decode. EditionSession::validate does not catch it, and core_2026_08_1_encoding_set_is_pinned builds a first-party-only session, so it does not either.
- A custom BtrBlocks scheme dropped by
retain_allowed_encodings, and an already-encoded custom array normalized away by the default pipeline, both produce a successful write with the caller's encoding absent and no diagnostic. A caller who explicitly configured a scheme arguably deserves an error rather than a warning.
VortexWriteOptions::new snapshots the editions for the strategy while write_internal re-resolves them for the array context. The comment at writer.rs:227 states that encodings registered in between are still eligible, but they are eligible to the context and not to the default validator, so one of the two comments is wrong.
What happened?
VortexWriteOptionstakes the file's array encoding allowlist entirely from the session's enabled editions, so an application cannot write an array encoding it registered itself. Registration and reads are unaffected.The gate is
new_array_context(vortex-file/src/writer.rs:358), built insidewrite_internaland reachable from no public API.with_strategydoes not replace it andWriteStrategyBuilder::with_allow_encodingscannot widen it, so an encoding outside the enabled editions fails atvortex-array/src/serde.rs:196withArray encoding app.identity not permitted by ctx.Refusing to serialize a component outside the selected editions is intended, tested, and published in
docs/specs/editions.md. The regression is the loss of the opt-out that #8855 and #8945 both documented as supported. #8946 made that opt-out unreachable while hardening the policy against replacement strategies, and #9364 later rewrote the docs to match the code. Nothing in #8946's description or review discussion mentions third-party or custom encodings.The documented opt-out, before and after #8946
#8855 introduced editions as documentation only, ahead of any code:
#8945 restated it in mechanical terms, one PR before the writer gate landed:
#8946 made both halves false. A session with no
EnabledEditionsvariable permits nothing rather than everything, which is why that PR had to addenable_all_registered_array_encodingsto the lower-level test sessions invortex-file,vortex-layoutandvortex-parquet-variant.with_allow_encodingscan only narrow, because the array context is resolved independently of it.#9364 deleted the paragraph and replaced it with the current text:
Steps to reproduce
Details
vortex/tests/third_party_encoding.rsonclaude/vortex-editions-third-party-0f81bvregisters a minimalapp.identityencoding on a default session and records what the writer does with it. All six cases pass as written, so the tests that assert a failure are the ones a fix must flip.serde.rs:196serde.rs:196serde.rs:196core2026.08.1widened bydeclare_inclusionRestoring the pre-#8946 registry-derived allowlist on
develop, with nothing else changed, flipscustom_encoding_is_rejected_when_it_is_in_no_editionandwith_allow_encodings_cannot_readmit_a_third_party_encodingto succeeding writes. That isolatesnew_array_contextas the cause.Environment
developas of 42a5d3b.Additional context
The workaround is for the application to declare and enable its own edition family, the way
vortex-spatialandvortex-jsondo. That path works and independent families coexist without coordination, but it is documented nowhere an application author will look.docs/specs/editions.mdpresents families as a first-party mechanism,docs/developer-guide/extending/writing-an-encoding.mdis a two-line stub, anddocs/developer-guide/language-bindings.mdstill advertises Tier 3 plugins with Rust already at Tier 3 and no mention of editions.Proposal: add an explicit per-write opt-in on
VortexWriteOptions, for examplewith_additional_encodings(HashSet<ArrayId>), unioned into both the strategy allowlist andnew_array_context. That keeps #8946's actual goal, since a replacement strategy still cannot widen anything on its own. If the project would rather not reopen the opt-out, the fix is documentation only, and the requirement belongs where plugin authors hit it.Three related defects turned up while tracing this, each worth a separate fix:
declare_inclusionhas no freeze check, so an application can add its encoding to frozencore2026.08.1and write a file that presents as an edition whose published minimum reader is0.84.0but that no released reader can decode.EditionSession::validatedoes not catch it, andcore_2026_08_1_encoding_set_is_pinnedbuilds a first-party-only session, so it does not either.retain_allowed_encodings, and an already-encoded custom array normalized away by the default pipeline, both produce a successful write with the caller's encoding absent and no diagnostic. A caller who explicitly configured a scheme arguably deserves an error rather than a warning.VortexWriteOptions::newsnapshots the editions for the strategy whilewrite_internalre-resolves them for the array context. The comment atwriter.rs:227states that encodings registered in between are still eligible, but they are eligible to the context and not to the default validator, so one of the two comments is wrong.