Tencent/WeKnora · error

%w: api_key must be a non-empty string

Error message

%w: api_key must be a non-empty string

What it means

parseNotionConfig requires the "api_key" credential to be a non-empty string. If the value exists but is not a string, or is the empty string, the config is rejected with ErrInvalidCredentials wrapped with "api_key must be a non-empty string". This catches mistyped config values (e.g. numbers, nil) and blank tokens.

Source

Thrown at internal/datasource/connector/notion/types.go:45

// Config holds Notion-specific configuration for the data source connector.
type Config struct {
	APIKey string `json:"api_key"` // Internal Integration Token
}

// parseNotionConfig extracts and validates Notion config from DataSourceConfig.
func parseNotionConfig(config *types.DataSourceConfig) (*Config, error) {
	if config == nil {
		return nil, datasource.ErrInvalidConfig
	}

	tokenVal, ok := config.Credentials["api_key"]
	if !ok {
		return nil, fmt.Errorf("%w: missing api_key", datasource.ErrInvalidCredentials)
	}
	token, ok := tokenVal.(string)
	if !ok || token == "" {
		return nil, fmt.Errorf("%w: api_key must be a non-empty string", datasource.ErrInvalidCredentials)
	}

	return &Config{APIKey: token}, nil
}

// --- API response types ---

// notionPage represents a Notion page or database object.
type notionPage struct {
	ID             string       `json:"id"`
	Object         string       `json:"object"` // "page" | "database" | "data_source" (2025-09-03+)
	Parent         notionParent `json:"parent"`
	URL            string       `json:"url"`
	LastEditedTime time.Time    `json:"last_edited_time"`
	InTrash        bool         `json:"in_trash"`
	// Title is extracted from properties after unmarshaling; not directly from JSON.
	Title string `json:"-"`
	// RawTitle holds the top-level title array (used by database objects).

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Set api_key to the full integration token string (starts with secret_)
  2. Check the env var actually has a value: echo "$NOTION_API_KEY"
  3. Fix config file syntax so api_key gets a real string value
  4. Run Validate first to fail fast with this message

Example fix

// before
token := os.Getenv("NOTION_API_KEY") // may be ""
// after
token := os.Getenv("NOTION_API_KEY")
if token == "" {
    log.Fatal("NOTION_API_KEY must be set")
}
Defensive patterns

Strategy: type-guard

Validate before calling

token, ok := config.Credentials["api_key"].(string); if !ok || token == "" { return errors.New("api_key must be a non-empty string") }

Type guard

func validAPIKey(cfg *datasource.Config) bool {
    if cfg == nil || cfg.Credentials == nil { return false }
    s, ok := cfg.Credentials["api_key"].(string)
    return ok && strings.TrimSpace(s) != ""
}

Try / catch

if err := connector.Validate(ctx, cfg); err != nil {
    if errors.Is(err, datasource.ErrInvalidCredentials) && strings.Contains(err.Error(), "non-empty string") {
        return errors.New("api_key is set but empty or wrong type")
    }
    return err
}

Prevention

When it happens

Trigger: config.Credentials["api_key"] holds a non-string (e.g. nil from a YAML null, a number) or an empty string, passed to Validate/ListResources/FetchAll/FetchIncremental.

Common situations: Env var set to empty string (exported but blank), secret manager returning nil, config YAML with `api_key:` (empty value), quoting errors in config files.

Related errors


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