gastownhall/beads · error

could not extract a Notion ID from %q

Error message

could not extract a Notion ID from %q

What it means

ResolveDataSourceReference first runs the reference string through ExtractNotionIdentifier to pull a 32-hex Notion ID (optionally dash-separated) out of a URL or raw string. If nothing ID-shaped is found it returns this error naming the original reference.

Source

Thrown at internal/notion/client.go:212

	RetrieveDataSource(ctx context.Context, dataSourceID string) (*DataSource, error)
	RetrieveDatabase(ctx context.Context, databaseID string) (*Database, error)
}

type ResolvedDataSource struct {
	InputID      string
	DataSourceID string
	DataSource   *DataSource
	Database     *Database
	ViewURL      string
}

func ResolveDataSourceReference(ctx context.Context, client DataSourceResolver, ref string) (*ResolvedDataSource, error) {
	if client == nil {
		return nil, fmt.Errorf("notion client is nil")
	}
	identifier := ExtractNotionIdentifier(ref)
	if identifier == "" {
		return nil, fmt.Errorf("could not extract a Notion ID from %q", ref)
	}
	if ds, err := client.RetrieveDataSource(ctx, identifier); err == nil {
		return &ResolvedDataSource{
			InputID:      identifier,
			DataSourceID: ds.ID,
			DataSource:   ds,
			ViewURL:      strings.TrimSpace(ref),
		}, nil
	} else {
		db, dbErr := client.RetrieveDatabase(ctx, identifier)
		if dbErr != nil {
			return nil, fmt.Errorf("resolve %q as data source: %w; as database: %v", ref, err, dbErr)
		}
		if len(db.DataSources) == 0 || strings.TrimSpace(db.DataSources[0].ID) == "" {
			return nil, fmt.Errorf("database %s has no child data sources", db.ID)
		}
		resolvedID := strings.TrimSpace(db.DataSources[0].ID)
		resolvedDS, err := client.RetrieveDataSource(ctx, resolvedID)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Copy the full URL via Notion's 'Copy link' so the trailing 32-hex ID is present.
  2. Extract the ID manually with a regex like [0-9a-f]{32} and pass the raw ID.
  3. Check the configured value in your config/env for truncation or a display-name substitution.
  4. Pre-resolve the reference with notion.ExtractNotionIdentifier in validation code before calling the resolver.

Example fix

// before
clientCfg.ViewURL = "https://notion.so/my-team-wiki" // no ID
// after
clientCfg.ViewURL = "https://notion.so/my-team-wiki-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d"
Defensive patterns

Strategy: validation

Validate before calling

if notion.ExtractNotionIdentifier(ref) == "" {
    return fmt.Errorf("%q does not contain a Notion ID", ref)
}

Type guard

func isNotionIDLike(s string) bool {
    s = strings.ReplaceAll(s, "-", "")
    if len(s) != 32 {
        return false
    }
    for _, r := range s {
        if !(r >= '0' && r <= '9' || r >= 'a' && r <= 'f') {
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: Passing a string containing no Notion identifier: an empty string, a bare database title, a non-Notion URL, a URL lacking any 32-character hex ID, or text where the ID was replaced by a workspace/page slug.

Common situations: Config values pasted as 'My Team Wiki' or a docs URL (notion.so/Team-Wiki-...) whose slug has no ID; using a redirected short link format; typos stripping the ID from the URL; storing a view name instead of the view URL.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d88653c5af4537a1. Report an issue: GitHub.