Skip to content

fix(agents): guard gemini schema properties against null#32332

Merged
steipete merged 2 commits intoopenclaw:mainfrom
webdevtodayjason:codex/vertex-null-object-32245
Mar 3, 2026
Merged

fix(agents): guard gemini schema properties against null#32332
steipete merged 2 commits intoopenclaw:mainfrom
webdevtodayjason:codex/vertex-null-object-32245

Conversation

@webdevtodayjason
Copy link
Contributor

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: Vertex/Gemini tool-schema handling can receive malformed schemas where properties is null/non-object, which can lead to downstream Object.* conversion failures in strict validators.
  • Why it matters: This failure mode is hard to trace from embedded-run summary logs and can abort Vertex-backed runs.
  • What changed: cleanSchemaForGemini now coerces invalid properties values to {} and keeps recursive schema cleaning behavior.
  • What did NOT change (scope boundary): No model routing/auth changes, no provider config changes, and no runtime transport changes.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Vertex/Gemini tool-schema sanitation is more defensive: malformed properties values are normalized to {} instead of passing through.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node v22.22.0 and Node v25.5.0
  • Model/provider: Google Gemini / Vertex schema cleaning path
  • Integration/channel (if any): N/A (unit tests)
  • Relevant config (redacted): N/A

Steps

  1. Run pnpm vitest run src/agents/schema/clean-for-gemini.test.ts src/agents/schema/clean-for-xai.test.ts.
  2. Run npx -y node@25.5.0 ./node_modules/vitest/vitest.mjs run src/agents/schema/clean-for-gemini.test.ts.

Expected

  • Gemini schema cleaning handles malformed properties robustly.

Actual

  • Pass. properties: null and non-object properties are coerced to {}.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: top-level and nested properties: null, and non-object properties values.
  • Edge cases checked: behavior on Node 25.5.0.
  • What you did not verify: live Vertex end-to-end gateway run with reporter environment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert commit fix(agents): guard gemini tool schema properties against null.
  • Files/config to restore: src/agents/schema/clean-for-gemini.ts.
  • Known bad symptoms reviewers should watch for: unexpected schema mutation where malformed properties was previously passed through.

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: Sanitizing malformed properties could hide upstream bad schema authorship.
    • Mitigation: Scope is narrow (only invalid values), and tests document exact coercion behavior.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 3, 2026

Greptile Summary

This PR adds a defensive guard in cleanSchemaForGeminiWithDefs so that a properties value that is null, a primitive, or an array is coerced to {} instead of being passed through verbatim to Gemini/Vertex schema validators. The old code's outer if (key === "properties" && value && typeof value === "object") would fall through to the catch-all else { cleaned[key] = value; } branch for falsy/invalid values, silently forwarding properties: null downstream and causing Object.* method crashes in strict provider validators.

Key points:

  • The restructuring is correct: the properties key is now handled as its own outer branch with an explicit fallback to {}.
  • The added !Array.isArray(value) check closes a secondary gap — arrays are objects, so the old check would have enumerated them with numeric keys via Object.fromEntries(Object.entries([])).
  • The three new unit tests cover the stated scenarios (top-level null, top-level non-object, nested null with a valid sibling).
  • One minor coverage gap: the properties: [] (array) case introduced by !Array.isArray(value) is not tested explicitly (see inline comment).

Confidence Score: 5/5

  • This PR is safe to merge — it is a narrow, well-scoped defensive fix with no behaviour change for valid schemas.
  • The change is minimal, isolated to a single function branch, backward-compatible for all well-formed schemas, and accompanied by targeted unit tests. The only gap is a missing test for the properties: [] array case that the new !Array.isArray guard also handles.
  • No files require special attention.

Last reviewed commit: df95058

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +15 to +22
it("coerces non-object properties to an empty object", () => {
const cleaned = cleanSchemaForGemini({
type: "object",
properties: "invalid",
}) as { properties?: unknown };

expect(cleaned.properties).toEqual({});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing test for array-valued properties

The new !Array.isArray(value) branch in cleanSchemaForGeminiWithDefs means properties: [] (or any array) is now coerced to {}. This is the correct behaviour — previously the old typeof value === "object" check would have let an array through and tried to enumerate it with Object.fromEntries(Object.entries([])) — but there's no test covering it. Consider adding a case alongside the existing non-object test:

Suggested change
it("coerces non-object properties to an empty object", () => {
const cleaned = cleanSchemaForGemini({
type: "object",
properties: "invalid",
}) as { properties?: unknown };
expect(cleaned.properties).toEqual({});
});
it("coerces non-object properties to an empty object", () => {
const cleaned = cleanSchemaForGemini({
type: "object",
properties: "invalid",
}) as { properties?: unknown };
expect(cleaned.properties).toEqual({});
});
it("coerces array properties to an empty object", () => {
const cleaned = cleanSchemaForGemini({
type: "object",
properties: [],
}) as { properties?: unknown };
expect(cleaned.properties).toEqual({});
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/schema/clean-for-gemini.test.ts
Line: 15-22

Comment:
**Missing test for array-valued `properties`**

The new `!Array.isArray(value)` branch in `cleanSchemaForGeminiWithDefs` means `properties: []` (or any array) is now coerced to `{}`. This is the correct behaviour — previously the old `typeof value === "object"` check would have let an array through and tried to enumerate it with `Object.fromEntries(Object.entries([]))` — but there's no test covering it. Consider adding a case alongside the existing non-object test:

```suggestion
  it("coerces non-object properties to an empty object", () => {
    const cleaned = cleanSchemaForGemini({
      type: "object",
      properties: "invalid",
    }) as { properties?: unknown };

    expect(cleaned.properties).toEqual({});
  });

  it("coerces array properties to an empty object", () => {
    const cleaned = cleanSchemaForGemini({
      type: "object",
      properties: [],
    }) as { properties?: unknown };

    expect(cleaned.properties).toEqual({});
  });
```

How can I resolve this? If you propose a fix, please make it concise.

@steipete steipete force-pushed the codex/vertex-null-object-32245 branch from df95058 to 95da268 Compare March 3, 2026 01:12
@steipete steipete merged commit ddd71bc into openclaw:main Mar 3, 2026
@steipete
Copy link
Contributor

steipete commented Mar 3, 2026

Landed via temp rebase onto main.

  • Gate:
    RUN v4.0.18 /Users/steipete/Projects/clawdbot2

✓ src/agents/schema/clean-for-gemini.test.ts (4 tests) 2ms

Test Files 1 passed (1)
Tests 4 passed (4)
Start at 01:12:18
Duration 207ms (transform 102ms, setup 117ms, import 7ms, tests 2ms, environment 0ms)

Thanks @webdevtodayjason!

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

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants