Skip to content

fix(core): harden context error text collection#4632

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
he-yufeng:fix/context-error-safe-collect
May 31, 2026
Merged

fix(core): harden context error text collection#4632
wenshao merged 1 commit into
QwenLM:mainfrom
he-yufeng:fix/context-error-safe-collect

Conversation

@he-yufeng

Copy link
Copy Markdown
Contributor

Summary

Fixes #4609.

The context-length classifier walks provider error objects to find useful text. Some provider/network errors can contain DOMException-like objects whose accessors throw when read outside their expected receiver. In the reported stack, that made the diagnostic helper fail while handling the original API connection error.

This patch makes the text collector skip throwing accessors and fall back to data descriptors when Object.entries() cannot safely enumerate an object. The original error message is still preserved when it is readable.

Testing

npm run test --workspace=packages/core -- contextLengthError.test.ts
npm run typecheck --workspace=packages/core
npx eslint packages/core/src/utils/contextLengthError.ts packages/core/src/utils/contextLengthError.test.ts --max-warnings 0
npx prettier --check packages/core/src/utils/contextLengthError.ts packages/core/src/utils/contextLengthError.test.ts
npm run build --workspace=packages/core
git diff --check

} catch {
const descriptors = Object.getOwnPropertyDescriptors(value);
return Object.values(descriptors)
.filter(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The fallback filter checks 'value' in descriptor (data-descriptor check) but does not verify descriptor.enumerable. The primary path (Object.entries) returns only enumerable own properties, but this fallback returns all own data properties including non-enumerable ones (e.g., Error.stack). This creates a behavioral inconsistency — when a single getter throws, non-enumerable properties that were previously invisible suddenly appear in the collected text.

Suggested change
.filter(
.filter(
(descriptor): descriptor is PropertyDescriptor & { value: unknown } =>
'value' in descriptor && descriptor.enumerable === true,
)

— qwen3.7-max via Qwen Code /review

try {
return Object.entries(value).map(([, nested]) => nested);
} catch {
const descriptors = Object.getOwnPropertyDescriptors(value);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Object.getOwnPropertyDescriptors(value) can itself throw on exotic objects (e.g., Proxy with throwing getOwnPropertyDescriptor trap). Neither this catch block nor collectStrings catches that failure, so the exception propagates uncaught to the public API. Wrapping the fallback in its own try/catch would make the function fully defensive:

Suggested change
const descriptors = Object.getOwnPropertyDescriptors(value);
function enumerableValues(value: object): unknown[] {
try {
return Object.values(value);
} catch {
try {
const descriptors = Object.getOwnPropertyDescriptors(value);
return Object.values(descriptors)
.filter(
(descriptor): descriptor is PropertyDescriptor & { value: unknown } =>
'value' in descriptor && descriptor.enumerable === true,
)
.map((descriptor) => descriptor.value);
} catch {
return [];
}
}
}

Note: this suggestion also uses Object.values() instead of Object.entries().map() (functionally identical, avoids intermediate tuple allocation) and includes the enumerable filter from the other suggestion.

— qwen3.7-max via Qwen Code /review

expect(info.isExceeded).toBe(false);
});

it('skips accessor properties that throw while collecting error text', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The existing throwing-accessor test covers Error instances, which exercise the safeReadProperty path. Non-Error plain objects with throwing accessors go through a completely different code path — enumerableValues directly — which has no test coverage. Consider adding a test for that path:

it('skips accessor properties that throw on non-Error objects', () => {
  const obj: Record<string, unknown> = {};
  Object.defineProperty(obj, 'detail', {
    enumerable: true,
    get() { throw new TypeError('accessor refused'); },
  });
  Object.defineProperty(obj, 'message', {
    enumerable: true,
    value: 'context_length_exceeded: too many tokens',
  });
  const info = getContextLengthExceededInfo(obj);
  expect(info.isExceeded).toBe(true);
  expect(info.message).toContain('context_length_exceeded');
});

— qwen3.7-max via Qwen Code /review

@he-yufeng he-yufeng force-pushed the fix/context-error-safe-collect branch from 7dcee0b to 6d9c5d9 Compare May 30, 2026 07:06
@he-yufeng

Copy link
Copy Markdown
Contributor Author

Updated the branch for the defensive enumeration follow-up.

Changes:

  • kept the descriptor fallback limited to enumerable data properties, matching Object.values / Object.entries behavior
  • wrapped Object.getOwnPropertyDescriptors in its own catch so exotic objects/proxies cannot escape the collector
  • added a non-Error throwing-accessor regression test

Validation:

  • npm run test --workspace=packages/core -- contextLengthError.test.ts
  • npm run typecheck --workspace=packages/core
  • npx eslint packages/core/src/utils/contextLengthError.ts packages/core/src/utils/contextLengthError.test.ts --max-warnings 0
  • npx prettier --check packages/core/src/utils/contextLengthError.ts packages/core/src/utils/contextLengthError.test.ts
  • npm run build --workspace=packages/core
  • git diff --check

@wenshao

wenshao commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Verification Report — PR #4632

Tested locally on: macOS Darwin 25.4.0 / Node.js v22.17.0
Commit: 6d9c5d954 (fix(core): harden context error text collection)
Method: tmux parallel execution of all PR test plan items

Test Results

# Check Result Details
1 npm run test --workspace=packages/core -- contextLengthError.test.ts ✅ PASS 27 tests passed, 2.6s
2 npm run typecheck --workspace=packages/core ✅ PASS tsc --noEmit — 0 errors
3 npx eslint (contextLengthError.ts + test) ✅ PASS 0 warnings, 0 errors
4 npx prettier --check ✅ PASS All matched files use Prettier code style
5 npm run build --workspace=packages/core ✅ PASS Build succeeded
6 git diff --check ✅ PASS No whitespace issues

Summary

All 6 test plan items pass cleanly. The fix correctly hardens collectText() against throwing accessors (DOMException-like objects) while preserving the original error message when readable.


Verified by wenshao

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No review findings. Downgraded from Approve to Comment: CI still running. — qwen3.7-max via Qwen Code /review

@BZ-D BZ-D left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. safeReadProperty + enumerableValues 能优雅地处理 DOMException 风格的 throwing getter,fallback 到 getOwnPropertyDescriptors 读 data property 是合理的 best-effort 策略。

@wenshao wenshao merged commit a3bc42d into QwenLM:main May 31, 2026
11 checks passed
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Getting [API Error: Value of "this" must be of DOMException] when trying to run a local model

3 participants