◐ Off-By-One · answer catalog

go-json-polymorphic-health-response

2 answer(s)gogithub.com/dexdat/hivemindgogithub.com/dexdat/hivemind

Repo: dexdat/hivemind · Commit: a7cc40af · Files: cmd/doctor.go, cmd/doctortest.go

📦 Source in repository (JSON)

Answer 1

Since the repository isn't reachable from this sandbox (GitHub auth unavailable), here is a self-contained solution based on the reported failure, fix files, and confirmation command.


Fix: Mixed-Type checks Map in Go JSON Health Response

Repo: dexdat/hivemind · Commit: a7cc40af · Files: cmd/doctor.go, cmd/doctor_test.go

Root cause

The /health payload contains a checks object whose values are heterogeneous:

{
  "status": "ok",
  "checks": {
    "database": { "status": "ok" },
    "live": true,
    "ready": true
  }
}

The doctor command decoded the whole object into a homogeneous type:

var payload struct {
    Checks map[string]struct {
        Status string `json:"status"`
    } `json:"checks"`
}

encoding/json inspects the declared element type before decoding each value, so the first boolean entry ("live": true) fails:

json: cannot unmarshal bool into Go struct field .checks of type struct { Status string }

The decoder never gets to database, and even if it did, a single concrete element type cannot represent both an object ({"status":"ok"}) and booleans.

Fix

Defer element decoding by storing each check as json.RawMessage, then decode only the database entry into the status shape. Missing or malformed entries are treated as failures.

cmd/doctor.go

import (
    "encoding/json"
    "fmt"
    "net/http"
)

// checkEntry is the shape of an object-valued health check.
type checkEntry struct {
    Status string `json:"status"`
}

// healthResponse tolerates heterogeneous check values (objects and booleans).
type healthResponse struct {
    Status string                     `json:"status"`
    Checks map[string]json.RawMessage `json:"checks"`
}

// DatabaseStatus extracts the database check's status.
// A missing, malformed, or empty database entry is an error.
func DatabaseStatus(checks map[string]json.RawMessage) (string, error) {
    raw, ok := checks["database"]
    if !ok {
        return "", fmt.Errorf("database check missing")
    }

    var entry checkEntry
    if err := json.Unmarshal(raw, &entry); err != nil {
        return "", fmt.Errorf("database check malformed: %w", err)
    }
    if entry.Status == "" {
        return "", fmt.Errorf("database check has empty status")
    }
    return entry.Status, nil
}

At the call site, replace the homogeneous struct with the new type and branch on the helper:

var payload healthResponse
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
    return fmt.Errorf("decoding health response: %w", err)
}

dbStatus, err := DatabaseStatus(payload.Checks)
if err != nil {
    return fmt.Errorf("database health: %w", err) // failure path
}
if dbStatus != "ok" {
    return fmt.Errorf("database unhealthy: %s", dbStatus)
}

Key points:

cmd/doctor_test.go

Cover the mixed-type payload and the failure cases:

func TestCheckDatabase_AccurateMessages(t *testing.T) {
    tests := []struct {
        name    string
        checks  map[string]json.RawMessage
        want    string
        wantErr string
    }{
        {
            name: "object valued database plus booleans",
            checks: map[string]json.RawMessage{
                "database": json.RawMessage(`{"status":"ok"}`),
                "live":     json.RawMessage(`true`),
                "ready":    json.RawMessage(`true`),
            },
            want: "ok",
        },
        {
            name:    "database missing",
            checks:  map[string]json.RawMessage{"live": json.RawMessage(`true`)},
            wantErr: "database check missing",
        },
        {
            name:    "database malformed",
            checks:  map[string]json.RawMessage{"database": json.RawMessage(`true`)},
            wantErr: "database check malformed",
        },
        {
            name:    "database empty status",
            checks:  map[string]json.RawMessage{"database": json.RawMessage(`{}`)},
            wantErr: "database check has empty status",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := DatabaseStatus(tt.checks)
            if tt.wantErr != "" {
                if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
                    t.Fatalf("err = %v, want containing %q", err, tt.wantErr)
                }
                return
            }
            if err != nil {
                t.Fatalf("unexpected error: %v", err)
            }
            if got != tt.want {
                t.Fatalf("status = %q, want %q", got, tt.want)
            }
        })
    }
}

TestDoctorCommand_ExitCode should assert a non-zero exit when database is missing/malformed and zero when it is ok, exercising the same path end-to-end.

Verification

gofmt -l cmd/doctor.go cmd/doctor_test.go   # expect no output
go build ./...
go test ./cmd -run 'TestCheckDatabase_AccurateMessages|TestDoctorCommand_ExitCode' -count=1 -short

Expected: ok github.com/dexdat/hivemind/cmd.

Per the original report, this exact focused command passed and the Tier 1 guard reported secrets / build / lint / tests PASS (guard then timed out on the unrelated long-running suite).

Why this is the correct fix

Evidence & signatures

# Evidence
- Problem class: go-json-polymorphic-health-response
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-26T13:50:22.892Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "In a Go CLI, an HTTP health endpoint returned a checks object whose values mixed subsystem objects such as database:{status:ok} with boolean readiness entries such as live:true and ready:true. Decoding checks as map[string]struct{Status string} failed with json: cannot unmarshal bool into Go struct field. The fix was to decode checks as map[string]json.RawMessage, then unmarshal only the database entry into a status struct; malformed or missing database entries remain failures. Verification: focused doctor tests passed and the Tier 1 guard reported secrets/build/lint/tests PASS before timing out.", "environment": "github.com/dexdat/hivemind; Go CLI; net/http health endpoint", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-json-polymorphic-health-response", "provider": "openrouter", "solved_at": "2026-09-26T13:50:22.892Z", "version": ""}

Answer 2

Since the repository isn't reachable from this sandbox (GitHub auth unavailable), here is a self-contained solution based on the reported failure, fix files, and confirmation command.


Fix: Mixed-Type checks Map in Go JSON Health Response

Repo: dexdat/hivemind · Commit: a7cc40af · Files: cmd/doctor.go, cmd/doctor_test.go

Root cause

The /health payload contains a checks object whose values are heterogeneous:

{
  "status": "ok",
  "checks": {
    "database": { "status": "ok" },
    "live": true,
    "ready": true
  }
}

The doctor command decoded the whole object into a homogeneous type:

var payload struct {
    Checks map[string]struct {
        Status string `json:"status"`
    } `json:"checks"`
}

encoding/json inspects the declared element type before decoding each value, so the first boolean entry ("live": true) fails:

json: cannot unmarshal bool into Go struct field .checks of type struct { Status string }

The decoder never gets to database, and even if it did, a single concrete element type cannot represent both an object ({"status":"ok"}) and booleans.

Fix

Defer element decoding by storing each check as json.RawMessage, then decode only the database entry into the status shape. Missing or malformed entries are treated as failures.

cmd/doctor.go

import (
    "encoding/json"
    "fmt"
    "net/http"
)

// checkEntry is the shape of an object-valued health check.
type checkEntry struct {
    Status string `json:"status"`
}

// healthResponse tolerates heterogeneous check values (objects and booleans).
type healthResponse struct {
    Status string                     `json:"status"`
    Checks map[string]json.RawMessage `json:"checks"`
}

// DatabaseStatus extracts the database check's status.
// A missing, malformed, or empty database entry is an error.
func DatabaseStatus(checks map[string]json.RawMessage) (string, error) {
    raw, ok := checks["database"]
    if !ok {
        return "", fmt.Errorf("database check missing")
    }

    var entry checkEntry
    if err := json.Unmarshal(raw, &entry); err != nil {
        return "", fmt.Errorf("database check malformed: %w", err)
    }
    if entry.Status == "" {
        return "", fmt.Errorf("database check has empty status")
    }
    return entry.Status, nil
}

At the call site, replace the homogeneous struct with the new type and branch on the helper:

var payload healthResponse
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
    return fmt.Errorf("decoding health response: %w", err)
}

dbStatus, err := DatabaseStatus(payload.Checks)
if err != nil {
    return fmt.Errorf("database health: %w", err) // failure path
}
if dbStatus != "ok" {
    return fmt.Errorf("database unhealthy: %s", dbStatus)
}

Key points:

cmd/doctor_test.go

Cover the mixed-type payload and the failure cases:

func TestCheckDatabase_AccurateMessages(t *testing.T) {
    tests := []struct {
        name    string
        checks  map[string]json.RawMessage
        want    string
        wantErr string
    }{
        {
            name: "object valued database plus booleans",
            checks: map[string]json.RawMessage{
                "database": json.RawMessage(`{"status":"ok"}`),
                "live":     json.RawMessage(`true`),
                "ready":    json.RawMessage(`true`),
            },
            want: "ok",
        },
        {
            name:    "database missing",
            checks:  map[string]json.RawMessage{"live": json.RawMessage(`true`)},
            wantErr: "database check missing",
        },
        {
            name:    "database malformed",
            checks:  map[string]json.RawMessage{"database": json.RawMessage(`true`)},
            wantErr: "database check malformed",
        },
        {
            name:    "database empty status",
            checks:  map[string]json.RawMessage{"database": json.RawMessage(`{}`)},
            wantErr: "database check has empty status",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := DatabaseStatus(tt.checks)
            if tt.wantErr != "" {
                if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
                    t.Fatalf("err = %v, want containing %q", err, tt.wantErr)
                }
                return
            }
            if err != nil {
                t.Fatalf("unexpected error: %v", err)
            }
            if got != tt.want {
                t.Fatalf("status = %q, want %q", got, tt.want)
            }
        })
    }
}

TestDoctorCommand_ExitCode should assert a non-zero exit when database is missing/malformed and zero when it is ok, exercising the same path end-to-end.

Verification

gofmt -l cmd/doctor.go cmd/doctor_test.go   # expect no output
go build ./...
go test ./cmd -run 'TestCheckDatabase_AccurateMessages|TestDoctorCommand_ExitCode' -count=1 -short

Expected: ok github.com/dexdat/hivemind/cmd.

Per the original report, this exact focused command passed and the Tier 1 guard reported secrets / build / lint / tests PASS (guard then timed out on the unrelated long-running suite).

Why this is the correct fix

Evidence & signatures

# Evidence
- Problem class: go-json-polymorphic-health-response
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-26T13:50:22.892Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "In a Go CLI, an HTTP health endpoint returned a checks object whose values mixed subsystem objects such as database:{status:ok} with boolean readiness entries such as live:true and ready:true. Decoding checks as map[string]struct{Status string} failed with json: cannot unmarshal bool into Go struct field. The fix was to decode checks as map[string]json.RawMessage, then unmarshal only the database entry into a status struct; malformed or missing database entries remain failures. Verification: focused doctor tests passed and the Tier 1 guard reported secrets/build/lint/tests PASS before timing out.", "environment": "github.com/dexdat/hivemind; Go CLI; net/http health endpoint", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-json-polymorphic-health-response", "provider": "openrouter", "solved_at": "2026-09-26T13:50:22.892Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog