Skip to content

[Bug]: patternProperties normalization misses outer unevaluatedProperties closure #4109

Closed

Description

@Ingwannu

Client or integration

Direct HTTP/API client

Area

Provider adapter

Summary

The correction merged in #4096 preserves a regex-keyed dictionary when its closing keyword is in the same schema object. It still removes the matcher when an outer schema closes the same object instance through unevaluatedProperties. That changes an otherwise usable tool schema into one that accepts no arguments. This is functional schema compatibility, not an authentication or exploitation report.

@lidge-jun This tracks the remaining request-changes review at the merged head 5e68c4e; the issue was not resolved by the direct-node closure fix.

Reproduction

Deterministic static path; not a claimed live-provider reproduction:

  1. A function tool has the following parameters, using a Unicode letter matcher supported by its ECMA-capable destination:
{
  "type": "object",
  "allOf": [{
    "patternProperties": {
      "^\\p{L}+$": { "type": "string" }
    }
  }],
  "unevaluatedProperties": false,
  "minProperties": 1
}
  1. stripUnicodePropertyPatterns walks into allOf. patternPropertyDropOnlyWidens sees neither additionalProperties nor unevaluatedProperties on the inner node and permits deleting its matcher.
  2. The outer unevaluatedProperties:false then has no matching-property evaluation from that branch. Every key is rejected, while minProperties:1 rejects the empty object.
  3. The legitimate control is a letter-keyed object such as {"label":"value"}, accepted by the original schema on an ECMA-capable destination.

Expected: preserve the dictionary's admissible argument contract, or leave the unsupported regex intact with an honest destination error. A node-local "only widens" assertion is not sufficient for same-instance composition.

Version

dev at b5c9833, September 9, 2026. This is a current-dev source finding, not a claim about an installed release.

Operating system

Static source review on Linux; the affected logic is platform-independent. No product execution was performed on the live local profile.

Provider and model

The shared openai-chat and openai-responses tool-schema paths; no model-specific live behavior is asserted.

Logs or error output

No runtime logs. Source: src/adapters/responses-tool-schema.ts, patternPropertyDropOnlyWidens and the patternProperties traversal in stripUnicodePropertyPatterns. Both adapters invoke this normalizer.

Screenshots and supporting files

Review on #4096: request-changes review 5153451095. Existing added tests cover direct closure, but not allOf with an outer unevaluatedProperties closure.

Redacted configuration

Only the synthetic schema above; no provider endpoint or credential is needed.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Suggested bounded next step: leave regex-keyed patternProperties intact in this patch while retaining the scalar pattern repair, or supply a composition-aware policy. Add the case above through both adapter buildRequest seams and retain the direct closed/open/literal-name controls. This needs an explicit policy decision; do not fix it by globally relaxing object closure.

Activity

  1. added
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    on Sep 9, 2026
  2. lidge-jun commented on Sep 9, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 74 / 80

    설명

    이 이슈(#4109, @Ingwannu, bug+provider)는 방금 dev에 올라간 #4096 tool-schema patternProperties 정규화의 남은 구멍이다. HEAD b5c98333f의 src/adapters/responses-tool-schema.ts는 Python re가 못 컴파일하는 유니코드 property 매처를 지울 때, patternPropertyDropOnlyWidens로 “이 노드에서 지우면 허용 집합이 넓어지기만 하는지”를 본다. 같은 객체에 additionalProperties / unevaluatedProperties가 닫혀 있으면 지우지 않는다. #4096이 직접 닫힘(같은 노드)은 고쳤다.

    남는 실패. 스키마가 allOf[{patternProperties}] + outer unevaluatedProperties:false + minProperties:1처럼 같은 인스턴스를 조합하면, 워커가 allOf 안으로 들어가 안쪽 노드만 본다. 안쪽에는 unevaluatedProperties가 없으니 drop이 허용되고, 바깥 닫힘이 그 매처 평가를 잃으면 모든 키가 거절되며 minProperties:1이 빈 객체도 거절한다. 원래 ECMA 쪽 목적지는 글자 키 객체({"label":"value"})를 받는데, 정규화 후에는 인자 없는 도구가 된다. openai-chat / openai-responses 둘 다 이 normalizer를 탄다. 인증/보안 보고가 아니라 스키마 호환성 이슈다. 제안: 이번 패치에서는 regex-keyed patternProperties를 그대로 두고 scalar pattern 수리만 유지하거나, composition-aware 정책을 따로 정한다. 전역으로 object closure를 풀지 말 것. allOf+outer unevaluatedProperties 케이스를 두 어댑터 buildRequest seam으로 고정하고, 직접 닫힘/열림/리터럴 이름 컨트롤은 유지.

    지금 dev 방향. #4096이 tip이고, 이 이슈는 그 request-changes 잔여(리뷰 5153451095, head 5e68c4ef6)다. 직접 노드 닫힘 수정으로는 해결되지 않았다. tip 슬라이스와 무관하고, 도구 스키마를 프록시하는 모든 클라이언트에 영향이 있다. types/config 분할·중복 close 대상 아님. 픽스 PR은 아직 없다.

    라인 - 이게 무슨 문제다

    src/adapters/responses-tool-schema.ts patternPropertyDropOnlyWidens (대략 36–38행) - 현재 노드의 additional/unevaluated만 본다. 부모 composition의 같은-인스턴스 닫힘을 모른다.

    src/adapters/responses-tool-schema.ts stripUnicodePropertyPatterns patternProperties 분기 (대략 209행 근처) - allOf 자식에서 dropOnlyWidens==true면 매처 삭제. outer unevaluatedProperties:false와 만나면 사전형 도구가 빈 객체 전용이 된다.

    테스트 - #4096이 추가한 직접 닫힘/열림/리터럴 이름 케이스는 있다. allOf + outer unevaluatedProperties는 없다. 이슈의 JSON fixture가 그 회귀 후보이다.

    정책 - “지원 안 하는 regex를 목적지에 그대로 보내 정직한 에러” vs “조합을 추적해 보존”. 전역으로 closure를 느슨하게 풀면 다른 스키마 계약이 흔들린다. 메인테이너 결정이 필요하다.

    메인테이너의 판단이 필요한 지점

    • 단기: regex-keyed patternProperties drop을 끄고 scalar pattern만 고칠지(이슈 제안, 안전).
    • 중기: parent/composition-aware “only widens”를 넣을지(비용·복잡도).
    • help wanted로 열지, fix(openai): drop tool-schema patterns Python re cannot compile #4096 직후 유지보수 PR로 묶을지.
    • openai-chat과 openai-responses 양 seam 회귀를 필수 조건으로 할지(권장).

    너의 추천

    받아라. 단기 정책은 “이 패치에서 regex-keyed patternProperties는 지우지 않기”가 가장 안전하다. scalar pattern 수리는 유지하고, allOf+outer unevaluatedProperties fixture를 두 어댑터에 추가한 뒤 랜딩. composition-aware 확장은 별 이슈/PR로 미뤄도 된다. tip 직후 기능 구멍이라 74. 중복 아님. close-don't-rebase 대상 아님.

    이 댓글은 grok-bot이 작성했습니다

  3. Ingwannu commented on Sep 9, 2026

    @Ingwannu
    OwnerAuthor

    Resolved on dev by #4113, merged by @lidge-jun at ad36c7be8913f4367fed0f3fa46acd2224914c7a. I verified that the reviewed head 9240bdec3b010e47761acb327cd75c8685b8f091 is contained in current origin/dev. The normalizer now preserves complete patternProperties/composition-sensitive subtrees, and both Chat and Responses buildRequest regressions preserve the allOf + outer unevaluatedProperties contract while retaining ordinary scalar-pattern repair. Exact-head Cross-platform CI run 34347174190 completed successfully; conditional skipped jobs are not counted as passed tests. This closes the reported defect in dev, not a claim that it has already shipped in a release.

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 workingproviderProvider adapters, OpenAI-compat presets, upstream API quirks

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions