googleapis/mcp-toolbox · error

invalid resource format at index %d, must be in the format o

Error message

invalid resource format at index %d, must be in the format of projects/{project_id_or_number}/locations/{location}/entryGroups/{group}/entries/{entry}

What it means

Each resource in dataplex-lookup-context must be a fully-qualified Dataplex entry name starting with projects/{id}/locations/{location}. Invoke splits each resource on '/' and rejects any entry with fewer than 4 segments or whose segments 0 and 2 are not 'projects' and 'locations', reporting the offending index.

Source

Thrown at internal/tools/dataplex/dataplexlookupcontext/dataplexlookupcontext.go:130

		return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, nil)
	}
	paramsMap := params.AsMap()
	resourcesSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["resources"].([]any), "string")
	if err != nil {
		return nil, util.NewAgentError(fmt.Sprintf("can't convert resources to array of strings: %s", err), err)
	}
	resources := resourcesSlice.([]string)

	if len(resources) == 0 {
		err := fmt.Errorf("resources cannot be empty")
		return nil, util.NewAgentError(err.Error(), err)
	}
	var name string
	var firstLocation string
	for i, resource := range resources {
		parts := strings.Split(resource, "/")
		if len(parts) < 4 || parts[0] != "projects" || parts[2] != "locations" {
			err := fmt.Errorf("invalid resource format at index %d, must be in the format of projects/{project_id_or_number}/locations/{location}/entryGroups/{group}/entries/{entry}", i)
			return nil, util.NewAgentError(err.Error(), err)
		}

		location := parts[3]
		if i == 0 {
			firstLocation = location
			project := source.ProjectID()
			name = fmt.Sprintf("projects/%s/locations/%s", project, location)
		} else if location != firstLocation {
			err := fmt.Errorf("all resources must belong to the same location. Please make separate calls for each distinct location")
			return nil, util.NewAgentError(err.Error(), err)
		}
	}

	resp, err := source.LookupContext(ctx, name, resources)
	if err != nil {
		return nil, util.ProcessGcpError(err)
	}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Pass the full resource name: projects/{project_id_or_number}/locations/{location}/entryGroups/{group}/entries/{entry}
  2. Use the exact resource name returned by dataplex-search-entries or dataplex-lookup-entry
  3. Check that segments are 'projects' and 'locations' (not 'region' or 'buckets') in order

Example fix

// before
{"resources": ["my-entry"]}
// after
{"resources": ["projects/my-proj/locations/us-central1/entryGroups/my-group/entries/my-entry"]}
Defensive patterns

Strategy: validation

Validate before calling

func validEntryName(r string) bool {
	parts := strings.Split(r, "/")
	return len(parts) >= 4 && parts[0] == "projects" && parts[2] == "locations"
}
for i, r := range resources {
	if !validEntryName(r) {
		return fmt.Errorf("resources[%d] is not a fully-qualified entry name", i)
	}
}

Type guard

func isFullyQualifiedEntryName(s string) bool {
	parts := strings.Split(s, "/")
	return len(parts) >= 4 && parts[0] == "projects" && parts[2] == "locations"
}

Try / catch

_, terr := tool.Invoke(ctx, src, params, token)
if terr != nil && strings.Contains(terr.Error(), "invalid resource format") {
	// re-resolve entry names via dataplex-search-entries, then retry
}

Prevention

When it happens

Trigger: resources[i] is a partial name like 'my-entry', 'projects/p/entries/e' (missing locations), a table/asset id, or a URL-encoded/wrongly-formatted path.

Common situations: Agent constructs entry names from truncated search results; user pastes only the entry id; region vs location segment confusion (e.g. 'regions/' instead of 'locations/').

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/1f6e8f4afd4c4886. Report an issue: GitHub.