chenhg5/cc-connect · error

received HTML response (likely missing auth); first 100 byte

Error message

received HTML response (likely missing auth); first 100 bytes: %s

What it means

After reading the body, downloadSlackFile sanity-checks whether Slack returned an HTML page (starting with <!DOCTYPE or <html) instead of the expected binary file content. This almost always means the request was not authenticated and Slack served a login/error page. The first 100 bytes are included to aid diagnosis.

Source

Thrown at platform/slack/slack.go:587

	if err != nil {
		return nil, fmt.Errorf("%s", core.RedactToken(err.Error(), p.botToken))
	}
	defer resp.Body.Close()

	// Check if we got an unexpected status code (e.g., redirect to login page)
	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
		return nil, fmt.Errorf("download failed with status %d: %s", resp.StatusCode, string(body))
	}

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response body: %w", err)
	}

	// Basic sanity check: detect if we received HTML instead of binary data
	if len(data) > 0 && (bytes.HasPrefix(data, []byte("<!DOCTYPE")) || bytes.HasPrefix(data, []byte("<html"))) {
		return nil, fmt.Errorf("received HTML response (likely missing auth); first 100 bytes: %s", string(data[:min(100, len(data))]))
	}

	return data, nil
}

func (p *Platform) ReconstructReplyCtx(sessionKey string) (any, error) {
	// slack:{channel}:{user}  |  slack:{channel}:t:{threadTS}  |  slack:{channel}
	parts := strings.SplitN(sessionKey, ":", 3)
	if len(parts) < 2 || parts[0] != "slack" {
		return nil, fmt.Errorf("slack: invalid session key %q", sessionKey)
	}
	rc := replyContext{channel: parts[1]}
	// Thread-scoped keys carry the thread root ts as a "t:<ts>" suffix; preserve
	// it so proactive replies (cron, send-to-session, restart/model/delete
	// notifications) post into the original thread instead of the channel root.
	if len(parts) == 3 && strings.HasPrefix(parts[2], "t:") {
		rc.timestamp = strings.TrimPrefix(parts[2], "t:")
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Ensure the request carries the bot token: pass slack.MsgOption or set Authorization: Bearer <token> on the http request using the file's url_private_download field
  2. Verify the bot token has the files:read scope (check via auth.test / app config)
  3. Use file.URLPrivateDownload, not file.Permalink or URLPrivate, for programmatic download
  4. Inspect the logged first-100-bytes snippet to confirm it is a login/permission page

Example fix

// before
req, _ := http.NewRequest("GET", fileURL, nil)
resp, err := http.DefaultClient.Do(req)
// after
req, _ := http.NewRequest("GET", fileURL, nil) // fileURL = file.URLPrivateDownload
req.Header.Set("Authorization", "Bearer "+p.botToken)
resp, err := http.DefaultClient.Do(req)
Defensive patterns

Strategy: validation

Validate before calling

func fileURLIsDownloadable(f *slack.File) bool {
    return f.URLPrivateDownload != "" // use this URL + Bearer token
}

Try / catch

data, err := downloadSlackFile(f.URLPrivateDownload, token)
if err != nil {
    if strings.Contains(err.Error(), "received HTML response") {
        return fmt.Errorf("slack auth/URL problem: %w", err) // fix token/URL, don't retry
    }
    return err
}

Prevention

When it happens

Trigger: The file URL requires auth but the request was sent without (or with an invalid/expired) Authorization: Bearer token; a redirect landed on a Slack sign-in HTML page; the URL is a browser page URL rather than the url_private_download endpoint.

Common situations: Using public_permalink instead of url_private_download; token lacking files:read scope; user token vs bot token mismatch; expired token after workspace app re-install; corporate proxy returning an HTML interstitial.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/6659fab2c914490f. Report an issue: GitHub.