glanceapp/glance · error

unmarshalling stream metadata: %w

Error message

unmarshalling stream metadata: %w

What it means

Emitted while parsing the 'StreamMetadata' half of the same Twitch GQL batched request. json.Unmarshal of the StreamMetadata payload into twitchStreamMetadataOperationResponse failed, meaning the returned JSON does not fit the expected struct. Like the channel shell error, this indicates Twitch altered the undocumented GQL schema.

Source

Thrown at internal/glance/widget-twitch-channels.go:162

		return result, err
	}

	if len(response) != 2 {
		return result, fmt.Errorf("expected 2 operation responses, got %d", len(response))
	}

	var channelShell twitchChannelShellOperationResponse
	var streamMetadata twitchStreamMetadataOperationResponse

	for i := range response {
		switch response[i].Extensions.OperationName {
		case "ChannelShell":
			if err = json.Unmarshal(response[i].Data, &channelShell); err != nil {
				return result, fmt.Errorf("unmarshalling channel shell: %w", err)
			}
		case "StreamMetadata":
			if err = json.Unmarshal(response[i].Data, &streamMetadata); err != nil {
				return result, fmt.Errorf("unmarshalling stream metadata: %w", err)
			}
		default:
			return result, fmt.Errorf("unknown operation name: %s", response[i].Extensions.OperationName)
		}
	}

	if channelShell.UserOrError.Type != "User" {
		result.Name = result.Login
		return result, nil
	}

	result.Exists = true
	result.Name = channelShell.UserOrError.DisplayName
	result.AvatarUrl = channelShell.UserOrError.ProfileImageUrl

	if channelShell.UserOrError.Stream != nil {
		result.IsLive = true
		result.ViewersCount = channelShell.UserOrError.Stream.ViewersCount

View on GitHub (pinned to 91324e8de7)

Solutions

  1. Log string(response[i].Data) at the failure point and compare with the struct fields to find the mismatch
  2. Adjust twitchStreamMetadataOperationResponse (nullable pointers for optional fields) in internal/glance/widget-twitch-channels.go
  3. Update Glance to the latest version where the fix has likely already landed

Example fix

// before
if err = json.Unmarshal(response[i].Data, &streamMetadata); err != nil {
    return result, fmt.Errorf("unmarshalling stream metadata: %w", err)
}

// after (tolerate null stream for offline channels)
type twitchStreamMetadataOperationResponse struct {
    User *struct {
        Stream *struct {
            ViewersCount int `json:"viewersCount"`
        } `json:"stream"`
    } `json:"user"`
}
Defensive patterns

Strategy: fallback

Type guard

func isTwitchUnmarshalErr(err error) bool {
	var syntaxErr *json.SyntaxError
	var typeErr *json.UnmarshalTypeError
	return errors.As(err, &syntaxErr) || errors.As(err, &typeErr)
}

Try / catch

if err != nil {
    if isTwitchUnmarshalErr(err) {
        slog.Warn("twitch stream metadata shape changed", "err", err)
        // proceed with offline/fallback channel rendering
    }
}

Prevention

When it happens

Trigger: A StreamMetadata operation response whose Data changed type or shape (e.g. stream field null for offline channels, viewer count becoming a string, removed nodes), causing Unmarshal to return a *json.UnmarshalTypeError.

Common situations: All Twitch channels in the widget show as offline/fail after a Twitch-side change; offline channels where stream metadata is null; running an old Glance version against a changed endpoint.

Related errors


AI-assisted analysis of glanceapp/glance@91324e8de7 (2026-08-15). Data as JSON: /api/errors/ce9c1fa3b47f5f30. Report an issue: GitHub.