On October 21 and 22, I spent two long days patching our own servers. PHP had announced an emergency release for two critical remote code execution bugs, now known as "PHP2Shell". We run seven RunCloud servers with about 30 WordPress sites, and several of them are on PHP versions that no longer get official fixes.
I did most of the work together with Hermes, my AI agent, running Claude Opus 5.5. It went well until it didn't. In the middle of the job, the cloud model stopped answering. My local model, Qwen3.8-Flash-Next on my own workstation, picked up the same session and finished the work.
This post covers what happened, why the cloud model stopped, and why I now see a local model as a real backup.
The work came in phases over two days:
Phase 3 is where it got stuck.
The agent had built the inventory: 32 sites, 206 plugins, 300 plugin/version pairs. It had matched them against the free WPVulnerability database and was checking access logs for suspicious requests to wp-content/uploads.
Then every message I sent got the same reply:
Your request was not processed. Send it again if you still want me to carry it out.
I tried "continue", "try it another way", and "try again, differently". The reply was the same each time. When I asked where it got stuck, it answered normally. It said it had not executed anything in those turns and could not see why. Its guess was a context length issue or an outage.
It wasn't either of those. The Hermes log had the real reason:
2026-10-22 14:49:28 WARNING [20261021_095417_816396] agent.conversation_loop:
Model declined to respond (finish_reason=content_filter).
model=claude-opus-5-5[1m] ...
refusal=This request triggered restrictions on violative cyber content and was
blocked under Anthropic's Usage Policy.
There were five of these in that session between 14:49 and 16:42, and one more in a separate session where I asked for a client report about the same breach. That report session was actually the first one I moved to the local model, and it finished the report there.
Claude Opus 5.5 runs behind safety classifiers. When one fires, the raw API returns a normal response with stop_reason: "refusal" and a category. Hermes logged it as finish_reason=content_filter, without the category field, but the refusal text points to the cyber category. Anthropic's refusals and fallback docs describe it like this: the category covers requests that "could enable cyber harm, such as malware or exploit development", and "benign cybersecurity work can also trigger this category."
That second sentence was us. Look at what was in the context window at that point:
code-execution.php in an uploads folder and ../../wp-config.php path traversal attemptsmaybe_unserialize() being replaced with unserialize( ..., array( 'allowed_classes' => false ) )
To a classifier, "read the vulnerable code path and figure out exactly how the bug works" looks the same whether you want to patch it or exploit it. My reading is that the classifier looks at the request as a whole, including the tool output the agent had just read. My message was "continue". The context was the problem.
A few details made it harder to see:
To be fair: this isn't a bug, and I understand why the classifier exists. The docs also describe a server-side fallbacks option that retries a refused request on a different Claude model. My setup goes through a subscription-based provider in Hermes that doesn't use it, so for me the session just stopped. If you build on the API directly, check whether your refusal category has a recommended fallback model, and log refusals as their own event. A refusal is an HTTP 200, so error-rate monitoring won't see it.
My workstation is a Ryzen 9 9950X3D with an RTX 5090 and 128 GB of RAM. I already run several large open models on it. I wrote about Qwen3.8-Flash-Next with Strata and a 5-bit quant and compared four local models side by side.
Hermes treats the local server as just another OpenAI-compatible provider:
providers:
strata-qwen:
name: Strata Qwen3.8-Flash-Next (UD-Q5_K_XL)
base_url: http://127.0.0.1:8081/v1
default_model: strata
models:
strata:
context_length: 524288
The switch took two steps:
/model and type (in Dutch): "Continue (now with a different model, hopefully no restrictions this time)."
Hermes keeps the session history, so Qwen picked up the full context: the inventory, the CVE list, the research that Opus and its subagents had already done, the plugin sources already downloaded, the earlier decisions, and my standing rules (no plugin updates, back up first, roll back on any error).
From 17:45 to 18:38, Qwen made 66 model calls. The prompt grew from about 121k to 212k tokens, well within the 512k context. Once the prompt cache was warm, turns took 3 to 20 seconds. About 20 turns missed the cache, mostly while it was writing the patcher, and took 70 to 115 seconds each. That's slower than the cloud, but it kept going.
In that time it:
patch_plugins.py) with exact code anchors, a HERMES-PATCH marker, and CRLF handling, and tested it locally on copies of each affected versionphp -l with each site's own PHP version, and automatic rollback127.0.0.1 only on the five servers that still listened on all interfaces. On two servers, the real setting turned out to be in a RunCloud file (mariadb.conf.d/99-server.cnf) that overrode the drop-in it had added first. It noticed, found the right file, and fixed it there.
It wasn't flawless. The patcher had two bugs: \$ in Python replacement strings, and a slug comparison that missed folder names with version numbers. It found both in its own local test run and fixed them. One count in its final summary (56 patched plugin copies) didn't match the earlier report (48), and I haven't fully reconciled that difference yet. Those are the kinds of things you check anyway, whatever model you use.
A local model is insurance. I don't run Qwen locally because it's better than Opus. For most work, I still use the cloud model. But the cloud model can stop on you for reasons you don't control: a classifier, an outage, a rate limit, a policy change. When that happens halfway through patching live servers, "come back later" isn't a good option. A local model with the same tools, the same agent, and the same session history means you can keep going.
A few practical points if you want this setup:
finish_reason=content_filter told me what had happened. Look for refusals separately from errors.
I expect cloud models to stay strict in some areas, and that's their call to make. Security work on your own servers will always look a bit like attack work. Having a capable model on your own hardware means that decision doesn't stop your work.