Tencent/WeKnora · error

create download request: %w

Error message

create download request: %w

What it means

DownloadFile builds the attachment download with http.NewRequestWithContext(ctx, GET, fileURL, nil); if http.NewRequestWithContext fails (it only fails on malformed URLs or a nil context), the error is wrapped as 'create download request'. Since the URL already passed SSRF validation, this almost always means the URL string itself is not parseable as an absolute http(s) URL.

Source

Thrown at internal/datasource/connector/notion/client.go:387

	}
	var block notionBlock
	if err := json.Unmarshal(respBody, &block); err != nil {
		return nil, fmt.Errorf("unmarshal block: %w", err)
	}
	return &block, nil
}

const maxDownloadSize = 100 * 1024 * 1024 // 100MB — prevent OOM from oversized files

// DownloadFile downloads a file from the given URL (typically an S3 signed URL).
// Does not go through the rate limiter since it's not a Notion API call.
func (c *notionClient) DownloadFile(ctx context.Context, fileURL string) ([]byte, error) {
	if err := utils.ValidateURLForSSRF(fileURL); err != nil {
		return nil, fmt.Errorf("attachment URL rejected: %w", err)
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileURL, nil)
	if err != nil {
		return nil, fmt.Errorf("create download request: %w", err)
	}

	var lastErr error
	for attempt := 0; attempt <= maxRetries; attempt++ {
		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = err
			if attempt < maxRetries {
				if sErr := sleepWithContext(ctx, time.Duration(1<<attempt)*time.Second); sErr != nil {
					return nil, sErr
				}
				continue
			}
			break
		}

		if resp.StatusCode != http.StatusOK {
			resp.Body.Close()

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Log the exact fileURL value in the wrapped error context and run url.Parse on it locally to see the parse failure.
  2. Ensure the URL is absolute with an http/https scheme; re-fetch the block (ResolveBlock) to get Notion's canonical signed URL instead of a stored/reconstructed one.
  3. Guard the call site: skip downloading when the block's file URL field is empty rather than passing "".
  4. Trim whitespace and reject empty/relative strings before calling DownloadFile.

Example fix

// before
client.DownloadFile(ctx, strings.TrimSpace(attachment.Path)) // "s3/bucket/file.pdf"
// after
if u, err := url.Parse(attachment.Path); err != nil || u.Scheme == "" {
    return fmt.Errorf("invalid attachment url %q", attachment.Path)
}
client.DownloadFile(ctx, attachment.Path)
Defensive patterns

Strategy: validation

Validate before calling

func validateDownloadURL(raw string) error {
    u, err := url.Parse(strings.TrimSpace(raw))
    if err != nil {
        return fmt.Errorf("unparsable attachment url: %w", err)
    }
    if u.Scheme == "" || u.Host == "" {
        return fmt.Errorf("attachment url must be absolute: %q", raw)
    }
    return nil
}

Type guard

func isAbsoluteHTTPURL(raw string) bool {
    u, err := url.Parse(raw)
    return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
}

Try / catch

data, err := client.DownloadFile(ctx, fileURL)
if err != nil {
    if strings.Contains(err.Error(), "create download request") {
        logger.Warnf(ctx, "malformed attachment url %q; re-fetching block", fileURL)
        return downloadAfterResolve(ctx, blockID)
    }
    return err
}

Prevention

When it happens

Trigger: Calling DownloadFile with a URL missing its scheme (e.g. "s3/bucket/file.pdf" or "/files/doc.pdf"), containing spaces or control characters, or otherwise unparsable by net/url. Also fires if ctx is nil.

Common situations: Storing attachment URLs in a database that stripped the https:// prefix; hand-built relative paths from a mirror config; template output with whitespace/quotes embedded in the URL; passing an empty string when the block's file URL field was absent.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/aef678c387c68bca. Report an issue: GitHub.