Summary
update_goal_plan is the only goal tool whose input schema is built from raw Zod v4 schemas instead of plain JSON Schema. When OpenCode serializes it for the model, Zod internals leak into the schema, including "type": "optional", which is not valid JSON Schema. Providers that validate tool schemas strictly reject the entire request.
Version / environment
@prevalentware/opencode-goal-plugin@0.1.59 (latest at time of writing)
- OpenCode v2
- Reproduced through the
opencode provider, whose upstream validates tool schemas with Python jsonschema
What happens
In dist/server.js, goalToolsV2() registers:
{
name: "update_goal_plan",
description: ...,
input: v2ObjectSchema(planToolArgs),
options: { codemode: false },
execute: async (args, context) => ({ content: await planFromTool(args, context) })
}
with
var planToolArgs = {
goal_id: z2.string().min(1),
expected_revision: z2.number().int().nonnegative(),
plan: GoalPlanInputSchema,
reason: z2.string().trim().min(1).max(2000),
revisit_evidence: z2.string().trim().min(1).max(2000).optional()
};
and
function v2ObjectSchema(properties, required = []) {
return {
type: "object",
properties,
required,
additionalProperties: false
};
}
v2ObjectSchema drops the raw Zod schemas straight into properties, so the tool input is a "JSON schema" whose property values are Zod instances. A Zod v4 schema serializes like this:
JSON.stringify(z.string().optional())
// {"~standard":{"vendor":"zod","version":1},"def":{"type":"optional","innerType":{"~standard":{...},"def":{"type":"string"},...}},"type":"optional"}
So the emitted schema contains revisit_evidence: { "def": {...}, "type": "optional" }.
Error observed
Upstream request failed: [invalid_request_error]
Error validating JSON Schema:
<ValidationError: "'optional' is not valid under any of the given schemas">
Reproduced locally with Python jsonschema 4.10.3:
from jsonschema import Draft202012Validator
list(Draft202012Validator(Draft202012Validator.META_SCHEMA).iter_errors({"type": "optional"}))
# MSG: 'optional' is not valid under any of the given schemas
Why only this tool
Every other goal tool builds input from plain JSON Schema helpers (e.g. v2GoalTextSchema(...), or inline { type: "string", enum: [...] }). update_goal_plan is the sole tool that passes the raw Zod planToolArgs through v2ObjectSchema.
Impact
Any OpenCode session that exposes update_goal_plan fails every model request when routed through a provider that validates tool schemas, because the provider rejects the whole request. The session cannot proceed.
Suggested fix
Build the update_goal_plan input with plain JSON Schema, matching the other tools:
goal_id, reason, revisit_evidence → { type: "string" }
expected_revision → { type: "integer", minimum: 0 }
plan → a fully expanded object schema (or convert with z.toJSONSchema(GoalPlanInputSchema) before wrapping)
Keep the Zod schemas for parsing/validation only (PlanToolSchema = z2.object(planToolArgs).strict()), but do not place them directly inside the tool input.
Summary
update_goal_planis the only goal tool whoseinputschema is built from raw Zod v4 schemas instead of plain JSON Schema. When OpenCode serializes it for the model, Zod internals leak into the schema, including"type": "optional", which is not valid JSON Schema. Providers that validate tool schemas strictly reject the entire request.Version / environment
@prevalentware/opencode-goal-plugin@0.1.59(latest at time of writing)opencodeprovider, whose upstream validates tool schemas with PythonjsonschemaWhat happens
In
dist/server.js,goalToolsV2()registers:with
and
v2ObjectSchemadrops the raw Zod schemas straight intoproperties, so the toolinputis a "JSON schema" whose property values are Zod instances. A Zod v4 schema serializes like this:So the emitted schema contains
revisit_evidence: { "def": {...}, "type": "optional" }.Error observed
Reproduced locally with Python
jsonschema4.10.3:Why only this tool
Every other goal tool builds
inputfrom plain JSON Schema helpers (e.g.v2GoalTextSchema(...), or inline{ type: "string", enum: [...] }).update_goal_planis the sole tool that passes the raw ZodplanToolArgsthroughv2ObjectSchema.Impact
Any OpenCode session that exposes
update_goal_planfails every model request when routed through a provider that validates tool schemas, because the provider rejects the whole request. The session cannot proceed.Suggested fix
Build the
update_goal_planinputwith plain JSON Schema, matching the other tools:goal_id,reason,revisit_evidence→{ type: "string" }expected_revision→{ type: "integer", minimum: 0 }plan→ a fully expanded object schema (or convert withz.toJSONSchema(GoalPlanInputSchema)before wrapping)Keep the Zod schemas for parsing/validation only (
PlanToolSchema = z2.object(planToolArgs).strict()), but do not place them directly inside the toolinput.