Skip to content

Handle accepted undefined fields during variant extraction - #6974

Merged
tim-smart merged 2 commits into
mainfrom
audit/repro-unstable-schema-variant-schema-undefined-field
Aug 4, 2026
Merged

Handle accepted undefined fields during variant extraction#6974
tim-smart merged 2 commits into
mainfrom
audit/repro-unstable-schema-variant-schema-undefined-field

Conversation

@fubhy

@fubhy fubhy commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

Variant extraction throws a native TypeError for direct or wrapped undefined fields that the public field types explicitly accept.

Important

This PR starts with focused failing reproduction tests. Add the implementation fix to this same branch; CI is expected to fail until that fix is included.

Accepted undefined fields crash variant extraction

Module: schema/VariantSchema
Audit ID: unstable-ai-cli-variant-schema-undefined-field-crash
Severity / confidence: medium / high

What happens

Variant extraction throws a native TypeError for direct or wrapped undefined fields that the public field types explicitly accept.

Why it happens

Extraction evaluates TypeId in value before checking value and can also pass an undefined wrapped schema to Schema.Struct.

Expected behavior

Struct.Fields and Field.Fields accept undefined so conditionally omitted fields can be represented without crashing extraction.

Relevant implementation

These links and excerpts are pinned to audit base c9b56ab507f224426ee8388dc450da447ec4715f.

View problematic code at packages/effect/src/unstable/schema/VariantSchema.ts:66-78
   * Field map accepted by a variant struct, where each property may be a schema, a
   * variant field, a nested struct, or `undefined`.
   *
   * @category models
   * @since 4.0.0
   */
  export type Fields = {
    readonly [key: string]:
      | Schema.Top
      | Field<any>
      | Struct<any>
      | undefined
  }

View exact lines on GitHub

View problematic code at packages/effect/src/unstable/schema/VariantSchema.ts:150-163
  /**
   * Field map whose properties may be schemas, variant fields, nested structs, or
   * `undefined`.
   *
   * @category models
   * @since 4.0.0
   */
  export type Fields = {
    readonly [key: string]:
      | Schema.Top
      | Field<any>
      | Struct<any>
      | undefined
  }

View exact lines on GitHub

View problematic code at packages/effect/src/unstable/schema/VariantSchema.ts:222-239
    const fields: Record<string, any> = {}
    for (const key of Object.keys(self[TypeId])) {
      const value = self[TypeId][key]
      if (TypeId in value) {
        if (options?.isDefault === true && Schema.isSchema(value)) {
          InternalRecord.assignProperty(fields, key, value)
        } else {
          InternalRecord.assignProperty(fields, key, extract(value, variant))
        }
      } else if (FieldTypeId in value) {
        if (Object.hasOwn(value.schemas, variant)) {
          InternalRecord.assignProperty(fields, key, value.schemas[variant])
        }
      } else {
        InternalRecord.assignProperty(fields, key, value)
      }
    }
    const schema = Schema.Struct(fields)

View exact lines on GitHub

Reproduction

pnpm test --run packages/effect/test/unstable/schema/VariantSchema.test.ts

Observed failure: FAIL: extraction threw a native TypeError.

Implementation handoff

The initial reproduction tests on this branch are the regression specification for the implementation fix that should follow in this PR.

  1. Start with the pinned implementation excerpts and the Why it happens analysis above.
  2. Change the implementation so it satisfies the stated Expected behavior; do not weaken or remove the reproduction assertions.
  3. Run the focused reproduction command(s) and confirm the observed failures become passing tests:
pnpm test --run packages/effect/test/unstable/schema/VariantSchema.test.ts
  1. Run the affected package's existing tests, then the repository lint and type checks before requesting review.

Audit provenance

  • Audit base: c9b56ab507f224426ee8388dc450da447ec4715f
  • Reproduction base: c9b56ab507f224426ee8388dc450da447ec4715f
  • Findings: unstable-ai-cli-variant-schema-undefined-field-crash
  • Initial patch: focused reproduction tests; implementation fix pending

Closes EFF-415

@fubhy fubhy added the audit Findings originating from the Effect runtime correctness audit label Aug 4, 2026
@changeset-bot

changeset-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 228c1eb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/ai-anthropic Patch
@effect/ai-openai Patch
@effect/ai-openai-compat Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node Patch
@effect/platform-node-shared Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/vitest Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-slopcop effect-slopcop Bot added 4.0 bug Something isn't working labels Aug 4, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

This PR is currently a repro-only commit: the title and commit message claim the bug is handled, but packages/effect/src/unstable/schema/VariantSchema.ts still throws. The new regression test fails against the unchanged implementation, so CI is red.

Reviewed changes

  • Added a focused regression test in packages/effect/test/unstable/schema/VariantSchema.test.ts covering VariantSchema.Struct fields declared as undefined.

The test asserts the correct behavior: a field whose value is undefined must be omitted from the extracted Schema.Struct. Before merging, the same branch needs the runtime fix in VariantSchema.extract (and a test covering the wrapped-undefined Field case).

⚠️ Missing implementation fix

VariantSchema.extract checks TypeId in value before verifying that value is defined. Because both Struct.Fields and Field.Config explicitly accept undefined, the in operator throws a native TypeError. A minimal fix is to skip the key when value === undefined before any branch inspection, and to do the same inside the Field branch when value.schemas[variant] is undefined.

Technical details
# Runtime crash on accepted `undefined` fields

## Affected sites
- `packages/effect/src/unstable/schema/VariantSchema.ts:225``TypeId in value` throws when `value` is `undefined`
- `packages/effect/src/unstable/schema/VariantSchema.ts:222-238``extract` does not short-circuit on `undefined` values

## Required outcome
- Extracting a `VariantSchema.Struct` whose `Fields` contain `undefined` values must omit those keys without throwing.
- Extracting a `Field` whose variant config maps the requested variant to `undefined` must also omit the field.
- Existing tests for non-undefined variants continue to pass.

## Suggested approach (optional)
At the top of the for-body in `extract`, skip when `value === undefined`. In the `FieldTypeId` branch, check that `value.schemas[variant]` is also `Schema.Top` before assigning it.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Comment thread packages/effect/test/unstable/schema/VariantSchema.test.ts
@tim-smart
tim-smart force-pushed the audit/repro-unstable-schema-variant-schema-undefined-field branch from 066b485 to 6c2ddf0 Compare August 4, 2026 22:42
@tim-smart
tim-smart force-pushed the audit/repro-unstable-schema-variant-schema-undefined-field branch from 6c2ddf0 to 228c1eb Compare August 4, 2026 22:42

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

The new commits since the prior review complete the implementation fix and extend test coverage.

  • Fixed VariantSchema.extract to skip undefined struct fields before inspecting TypeId or FieldTypeId, and to skip Field variant mappings that resolve to undefined.
  • Added a regression test for the wrapped-undefined Field case requested in the previous review.
  • Adjusted Struct.Validate to return unknown for non-struct/non-field entries and widened Field.ConfigWithKeys to Schema.Top | undefined, matching the runtime acceptance of undefined.
  • Added a patch changeset for the fix.

Prior pullfrog feedback has been addressed.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@tim-smart
tim-smart enabled auto-merge (squash) August 4, 2026 23:19
@tim-smart
tim-smart merged commit 4158562 into main Aug 4, 2026
18 of 19 checks passed
@tim-smart
tim-smart deleted the audit/repro-unstable-schema-variant-schema-undefined-field branch August 4, 2026 23:39
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 7.06 KB 7.06 KB 0.00 KB (0.00%)
batching.ts 9.86 KB 9.86 KB 0.00 KB (0.00%)
brand.ts 6.34 KB 6.34 KB 0.00 KB (0.00%)
cache.ts 10.71 KB 10.71 KB 0.00 KB (0.00%)
config.ts 20.60 KB 20.60 KB 0.00 KB (0.00%)
differ.ts 20.20 KB 20.20 KB 0.00 KB (0.00%)
http-client.ts 21.58 KB 21.54 KB +0.04 KB (+0.18%)
logger.ts 10.84 KB 10.84 KB 0.00 KB (0.00%)
metric.ts 8.98 KB 8.98 KB 0.00 KB (0.00%)
optic.ts 7.18 KB 7.18 KB 0.00 KB (0.00%)
pubsub.ts 14.99 KB 14.99 KB 0.00 KB (0.00%)
queue.ts 11.66 KB 11.66 KB 0.00 KB (0.00%)
schedule.ts 10.83 KB 10.83 KB 0.00 KB (0.00%)
schema-class.ts 19.14 KB 19.14 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.96 KB 28.96 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 25.29 KB 25.29 KB 0.00 KB (0.00%)
schema-string-transformation.ts 13.38 KB 13.38 KB 0.00 KB (0.00%)
schema-string.ts 10.94 KB 10.94 KB 0.00 KB (0.00%)
schema-template-literal.ts 15.17 KB 15.17 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 21.94 KB 21.94 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.34 KB 24.34 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 19.18 KB 19.18 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 19.01 KB 19.01 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.87 KB 18.87 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.60 KB 22.60 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.52 KB 19.52 KB 0.00 KB (0.00%)
schema.ts 18.41 KB 18.41 KB 0.00 KB (0.00%)
stm.ts 12.63 KB 12.63 KB 0.00 KB (0.00%)
stream.ts 9.80 KB 9.80 KB 0.00 KB (0.00%)

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

Labels

4.0 audit Findings originating from the Effect runtime correctness audit bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants