Skip to content

v1.8.5 — the two fixes v1.8.4 announced now actually work

Choose a tag to compare

@zw008 zw008 released this 20 Jul 13:04

v1.8.5 (2026-07-20) — the two fixes v1.8.4 announced now actually work

Four adversarial reviews of v1.8.4 found that both of its headline fixes were
incomplete in ways the release notes did not reflect. This release makes them
real. If you are on 1.8.4, this is the one to take.

Fixed — a failure that was returned was still audited as a success

vmware-policy 1.8.4 added report_tool_failure() for tools that catch an
exception and return an error payload instead of raising. No skill called it.

Every string-returning tool therefore kept doing exactly what 1.8.4 said it had
stopped doing: writing status=ok to ~/.vmware/audit.db for an operation that
failed, recording an undo token for a change that never happened, and telling the
circuit breaker the call succeeded so repeated failures never tripped it.

The surface this covered is not marginal:

Skill What was mis-audited
vmware-aiops 25 of 49 tools, including every undo-bearing write — a failed vm_power_on left an undo token saying "power it back off"
vmware-avi all 28 tools, including vs_toggle and ako_restart
vmware-storage all 4 write tools
vmware-nsx the 5 delete tools

vmware-avi is worth calling out: before 1.8.4 its exceptions propagated and the
audit was correct. 1.8.4 caught them and returned a string, so that release made
its audit trail worse than it had been.

Skills whose tools already return dict payloads (vmware-monitor, vmware-vks,
vmware-aria, vmware-log-insight, vmware-harden, vmware-debug, vmware-pilot) were
already detected correctly. They gained a test proving it rather than a redundant
call.

Fixed — narrowing OSError did not close the leak it was meant to close

1.8.4 narrowed the _safe_error passthrough because bare OSError let TLS and
DNS failures reach the agent with hostnames and certificate subjects in them.
That narrowing had no effect on the error it was written for:

ssl.SSLCertVerificationError → ssl.SSLError → OSError, ValueError

ValueError has been on every allowlist since long before 1.8.4, so a
certificate failure kept passing through — the commonest self-signed-certificate
failure in this family, carrying the hostname it was checked against. An
allowlist structurally cannot express "not this one".

Where ssl.SSLError can actually surface — the pyVmomi skills — it is now
reduced ahead of the allowlist. In the httpx skills TLS arrives wrapped as
httpx.ConnectError, and in vmware-avi as requests.exceptions.SSLError, so the
guard cannot fire there; in those skills the leak was the raw exception
interpolated into an already-allowlisted *ApiError, and that is now authored
text naming the config target and verify_ssl instead of the exception.

The missing-password error — this family's most common first-run failure, whose
entire remedy is the environment variable name it carries — keeps its message
through a narrow ConfigError(OSError) rather than the base class. Connection
failures are translated at the connection layer into an authored remedy that
names the target and the setting to change, with the raw detail left on
__cause__ for the server log.

Also fixed

  • vmware-vks: the quickstart documented a password variable the code never
    reads — following README.md verbatim produced "Password not found". Five
    places, plus six references to a doctor command this CLI has never had, two
    descriptions promising fields the tools do not return, and eight teaching
    messages that RuntimeError was masking.
  • vmware-nsx: an error cited --route-advertisement; the flag is --advertise.
  • vmware-pilot: get_workflow_status told the model to call approve — a
    tool the read-only gate withholds — as the required next step; and a hint
    pointed at a filename that could never appear in that message.
  • vmware-aiops: vm_task_status polling a failed task returned
    {"state": "error", "error": ...} from a successful read, which the new
    detection read as the call itself failing. The field is now task_error.
    This is a breaking change for anything parsing that payload.
  • Several remedies that were still being cut by the 300-character cap the 1.8.4
    notes claimed to have addressed.

Known and not fixed

ConnectionError remains one type from two sources in several skills — a
skill's own authored message and urllib3's HTTPSConnectionPool(host=..., port=...)
share it, and an allowlist cannot separate them. vmware-vks is converted; the
rest need their own domain type and are deferred rather than half-done.

v1.8.4 (2026-07-20) — errors that teach, and tool descriptions a small model can route from

A capability eval was rolled out across the family and asked two open questions:
when a call fails, is the model told enough to fix it, and can it pick the right
tool from the description alone? Both answers were worse than anyone thought, and
in several places the reason was that the measurement was looking somewhere other
than where the model reads.

Fixed — teaching messages were being discarded on the way to the agent

_safe_error reduces unrecognised exceptions to "<Class>: operation failed."
so raw API text, credentials in URLs and internal paths cannot reach an agent.
Its allowlist held only the builtin validation errors — so this skill's own
domain exceptions, the ones that exist precisely to carry a corrected next step,
had their messages replaced by their class names.

The effect was invisible from the CLI, which prints those messages in full.

The worst case was shared by nine skills: config.py raises exactly one
OSError, the missing-password error, whose entire remedy is the environment
variable name it names. An agent hitting an unconfigured target received
OSError: operation failed. and had nothing to act on. That is the family's most
common first-run failure, and it landed one release after the documented variable
names were corrected — so the message that would have unstuck the operator was
the one being thrown away.

The rule is now the property it always meant: every exception this skill raises
on purpose passes through
, and only genuinely unplanned ones are reduced.
RuntimeError stays reduced — it is the generic catch-all and in several skills
carries raw upstream text.

Fixed — error messages now carry the correction

Every message that reported a failure without saying how to recover was
rewritten: it names the offending value, gives an imperative remedy, and names
something concrete to act on — a tool that exists, a real CLI command, a config
file, an environment variable. Recovery becomes an instruction-following problem
rather than an inference one, which is what a weak model can still do.

Three classes of defect surfaced while doing it:

  • Remedies that were never delivered. _safe_error truncates with no
    ellipsis, so a message longer than the cap loses its closing sentence
    silently. One message had been shipping at 396 characters against a 300-char
    cap — its remedy had never once reached an agent. Messages now lead with the
    remedy so a long interpolated value truncates the expendable detail instead.
  • Commands that do not exist. One skill's error hints named a doctor
    subcommand it does not have.
  • Tools that do not exist. A tool description pointed at two sibling-skill
    tools that had been renamed, and another named a tool that had moved to a
    different skill entirely.

Improved — tool descriptions state when to use them and what to call next

The description is the API for a small model: an unstated routing rule is a
routing rule that does not exist, and a tool with no stated next hop is one the
model stops at. Descriptions now say when to prefer this tool over a sibling,
what shape comes back, the caveat that bites, and which tool to call after.

Manifest size did not grow. Descriptions load into every session, so the
routing clauses were paid for by cutting duplicated reference material —
repeated boilerplate, examples that restated the parameter list, and prose
copies of the pagination contract.

Note

Every tool and CLI command named anywhere in this release was verified against
the live MCP registry and the live command tree, not against documentation.