[MODEL]
Preflight Checklist
- [x] I have searched existing issues for similar behavior reports
- [x] This report does NOT contain sensitive information (API keys, passwords, etc.)
Type of Behavior Issue
Claude made incorrect assumptions about my project
What You Asked Claude to Do
Claude consistently presents hypotheses as verified conclusions, skipping the "design a test to falsify this" step. It's not a one-off reasoning error — it's a structural pattern across models and releases that degrades reliability for technical debugging work, where the cost of a confident wrong answer is high.
What Claude Actually Did
When debugging a NotAllowedError on sendReport in a Chrome WebHID extension, Claude observed that the failing device had usagePage=0x000C (Consumer Controls) and the working device had usagePage=0xFF00 (Vendor). Claude concluded: "Chrome intentionally blocks writes to Consumer Controls devices as a security policy." It stated this confidently, with a formatted results table, and recommended abandoning the approach for that device.
The actual cause was a report size mismatch — Chrome enforces the byte count declared in the HID descriptor (10 bytes), while the code was sending 63 bytes. One line change falsified the entire conclusion immediately.
This is not an isolated incident. The pattern is consistent across sessions, models, and releases: Claude reasons from observation to a plausible explanation, then presents that explanation as fact — skipping the step of designing a test to falsify it before concluding. In technical debugging this is high-cost: it causes real work to be abandoned, wrong architectural decisions to be made, and trust to erode.
Expected Behavior
"One hypothesis is that Chrome blocks writes to Consumer devices as a policy. Before concluding that, let's test it — change the report size to match the declared descriptor size and see if the error changes."
Impact: Wasted debugging time, incorrect architectural conclusions presented with confidence, user has to push back repeatedly to get to the actual root cause.
Files Affected
Permission Mode
Accept Edits was OFF (manual approval required)
Can You Reproduce This?
Sometimes (intermittent)
Steps to Reproduce
Steps to reproduce:
- Build a Chrome WebHID extension that connects to a HID device and calls device.sendReport()
- The call fails with NotAllowedError
- Note that the failing device has usagePage=0x000C (Consumer Controls); a working device has usagePage=0xFF00 (Vendor Defined)
- Ask Claude to diagnose the failure
Claude's response: Asserts that Chrome blocks writes to Consumer Controls devices as a security policy. States this as fact, provides a formatted summary table showing the device as permanently unsupported, and recommends an architectural pivot (e.g. switch to Electron).
What actually reproduces the correct behavior: Ask Claude "are you sure?" or manually try changing the report data size to match the HID descriptor's declared size. The NotAllowedError resolves immediately — the cause was a byte count mismatch, not a platform policy.
The reproducibility is not specific to this scenario. Present Claude with any ambiguous technical failure where multiple explanations are plausible. Claude will select the most pattern-matched explanation and state it as a conclusion rather than a hypothesis, without proposing a discriminating test first.
Claude Model
Sonnet
Relevant Conversation
The Tanchjim does declare output report 0x4B. The earlier NotAllowedError may have been a casualty of the JavaScript scope bugs
corrupting the flow, not a real hardware/Chrome limit.
Try the Tanchjim now with the clean code — connect it, click Request Device, select it, then Send Read Command.
❯ Requesting HID device...
Got 1 interface(s):
TANCHJIM-FISSION DSP VID=0x31b2 pages=[0xc]
Selected interface: TANCHJIM-FISSION DSP VID=0x31b2
collection usagePage=0xc usage=0x1
inputReports: id=0x54, id=0x4b, id=0x1
outputReports: id=0x54, id=0x4b
Opened. Click Send Read Command.
→ SENDING reportId=0x4b
000: 65 00 00 00 52 00 00 00 00 00 00 00 00 00 00 00
010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
sendReport failed: NotAllowedError: Failed to write the report.
sendFeatureReport failed: NotAllowedError: Failed to write the feature report.
⏺ Output report 0x4B is declared, yet Chrome still blocks it. The Qudelix (0xFF00) writes fine. The only difference is the usage
page.
Chrome intentionally blocks writes to usagePage=0x000C (Consumer Controls) — regardless of what the HID descriptor declares.
It's a security policy to prevent extensions from injecting fake media key events. The Tanchjim presents itself as a Consumer
device, so writes are blocked at the Chrome level.
Final spike results:
┌──────────────────────┬─────────────────┬────────────┬───────────────────────────────────────────────┐
│ Device │ Interface │ Write │ Reason │
├──────────────────────┼─────────────────┼────────────┼───────────────────────────────────────────────┤
│ Qudelix-5K │ 0xFF00 vendor │ ✅ Works │ Vendor-defined, no Chrome policy │
├──────────────────────┼─────────────────┼────────────┼───────────────────────────────────────────────┤
│ Tanchjim Fission DSP │ 0x000C consumer │ ❌ Blocked │ Chrome security policy on Consumer usage page │
└──────────────────────┴─────────────────┴────────────┴───────────────────────────────────────────────┘
The Chrome Extension path supports Qudelix, not Tanchjim.
For Tanchjim you'd need native code (Electron + node-hid, or a Native Messaging host). How do you want to handle it —
Qudelix-only for the extension, or do you need both?
❯ I don't believe you given how frequently you've been so confidently wrong recently
⏺ Fair. I've asserted things confidently that turned out to be wrong. I don't actually know that Chrome blocks 0x000C writes as
policy — that's a plausible explanation, not a verified one.
What I do know: report 0x4B is declared as an output report, and sendReport still fails. One thing I haven't checked: the
declared report size. Chrome may reject the write if the byte count doesn't match what the descriptor specifies. We're sending
63 bytes — but we don't know what size the descriptor actually declares for report 0x4B.
Let me log the actual report sizes:
⏺ Update(chrome-extension/popup.js)
⎿ Added 1 line, removed 1 line
77 // Log declared reports
78 for (const col of (device.collections || [])) {
79 log(` collection usagePage=0x${col.usagePage.toString(16)} usage=0x${(col.usage||0).toString(16)}`);
80 - const fmt = r => `id=0x${r.reportId.toString(16)}`;
80 + const fmt = r => `id=0x${r.reportId.toString(16)} size=${r.items?.reduce((s, i) => s + (i.reportCount ?? 0) *
+ (i.reportSize ?? 0), 0) ?? '?'}bits`;
81 if (col.inputReports?.length) log(` inputReports: ${col.inputReports.map(fmt).join(', ')}`, 'hex');
82 if (col.outputReports?.length) log(` outputReports: ${col.outputReports.map(fmt).join(', ')}`, 'hex');
83 if (col.featureReports?.length) log(` featureReports: ${col.featureReports.map(fmt).join(', ')}`, 'hex');
⏺ Reload, connect Tanchjim, and share what the outputReports line shows for the size of report 0x4B.
❯ outputReports: id=0x54 size=80bits, id=0x4b size=80bits
⏺ 80 bits = 10 bytes. We're sending 63. Chrome is likely rejecting it because the size doesn't match the declared report size. Try
with exactly 10 bytes:
⏺ Update(chrome-extension/popup.js)
⎿ Added 1 line, removed 1 line
116 data[0] = 0x05; data[1] = 0x80; data[2] = 0x01; data[6] = 0x04;
117 } else if (isTanchjim) {
118 reportId = 0x4B;
119 - data = new Uint8Array(63);
119 + data = new Uint8Array(10); // 80 bits as declared in HID descriptor
120 data[0] = 0x65; data[4] = 0x52;
121 } else {
122 log('Unknown device.', 'err'); return;
⏺ Reload and try. If sendReport OK appears, size mismatch was the problem all along — and we can read back the response to confirm
the protocol works.
❯ see
Click "Request Device" to begin...\nRequesting HID device...
Got 1 interface(s):
TANCHJIM-FISSION DSP VID=0x31b2 pages=[0xc]
Selected interface: TANCHJIM-FISSION DSP VID=0x31b2
collection usagePage=0xc usage=0x1
inputReports: id=0x54 size=80bits, id=0x4b size=80bits, id=0x1 size=8bits
outputReports: id=0x54 size=80bits, id=0x4b size=80bits
Opened. Click Send Read Command.
→ SENDING reportId=0x4b
000: 65 00 00 00 52 00 00 00 00 00
sendReport OK
← INPUT reportId=0x4b (10 bytes)
000: 65 00 00 00 52 00 f4 00 00 00
⏺ Both devices work. The problem all along was report size — Chrome enforces the HID descriptor size strictly (10 bytes), Python
ignores it and sends 64.
Parsing the response 65 00 00 00 52 00 f4 00 00 00:
- bytes[0] = 0x65 → FieldID = pregain ✓
- bytes[4] = 0x52 → READ command echo ✓
- bytes[6] = 0xf4 = 244 unsigned = -12 signed → pregain = -12 / 2 = -6.0 dB ✓
Spike complete. Chrome Extension works for both devices.
┌──────────────────────┬─────────────────┬─────────────┬────────┐
│ Device │ Interface │ Report size │ Result │
├──────────────────────┼─────────────────┼─────────────┼────────┤
│ Qudelix-5K │ 0xFF00 vendor │ 63 bytes │ ✅ │
├──────────────────────┼─────────────────┼─────────────┼────────┤
│ Tanchjim Fission DSP │ 0x000C consumer │ 10 bytes │ ✅ │
└──────────────────────┴─────────────────┴─────────────┴────────┘
The entire earlier investigation into "Chrome blocks Consumer devices" was wrong. It was always just a size mismatch. Sorry for
the runaround.
Next: commit this spike and plan the full extension build?
✻ Worked for 37s
❯ can you commit to memory that you keep assertively stating incorrect assumptions, like this recent one that again proved
false under a single actual test
Impact
High - Significant unwanted changes
Claude Code Version
2.1.50 (Claude Code)
Platform
Anthropic API
Additional Context
Happens across projects, models and releases. this seems to be inherent behavior of all models at the moment.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗