The console says an account was refused only when the bus refused it

"authorization" alone matched a tool's own answer mentioning the word — the
runtime refusing a state value with an Authorization header read as
"this account may not call".
This commit is contained in:
jochen
2026-10-04 11:31:42 +02:00
parent 670a7884ff
commit 8405e32efc
2 changed files with 27 additions and 2 deletions
+5 -2
View File
@@ -110,8 +110,11 @@ func subjectListed(name, node string, l *Listing) string {
var (
noResponders = regexp.MustCompile(`(?i)no responders|503`)
refused = regexp.MustCompile(`(?i)permissions violation|authorization`)
timedOut = regexp.MustCompile(`(?i)timeout`)
// The bus's own two refusals, by their whole phrase. "authorization" alone also matched a tool's own
// answer that merely mentions the word — the runtime refusing a state value with an Authorization
// header (novox/hq ADR 0201) read as "this account may not call", which sent the reader the wrong way.
refused = regexp.MustCompile(`(?i)permissions violation|authorization violation`)
timedOut = regexp.MustCompile(`(?i)timeout`)
)
// whyItFailed says why a call failed, so the remedy is in the words.
@@ -0,0 +1,22 @@
package console
import (
"errors"
"strings"
"testing"
)
// A refusal is said only for the bus's own refusals; a tool's answer that mentions authorization is the
// tool's answer, not the account's.
func TestARefusalIsSaidOnlyForTheBussOwn(t *testing.T) {
for msg, refusal := range map[string]bool{
`nats: Permissions Violation for Publish to "mesh.mod.x.tool.y"`: true,
"nats: Authorization Violation": true,
`claude-code's servers.all.x carries a field "Authorization", which names a credential`: false,
} {
got := strings.HasPrefix(whyItFailed("x.y", errors.New(msg)), "this account may not call")
if got != refusal {
t.Errorf("%q read as a refusal: %v, want %v", msg, got, refusal)
}
}
}