googleapis/mcp-toolbox · error

invalid writeMode %q: must be one of %q, %q, or %q

Error message

invalid writeMode %q: must be one of %q, %q, or %q

What it means

The BigQuery source validates the writeMode config field during Initialize. Only 'allowed', 'blocked', or 'protected' are accepted; any other string fails this check right after the readOnly-defaulting logic. This prevents silent typos that would otherwise make write behavior undefined.

Source

Thrown at internal/sources/bigquery/bigquery.go:148

	}
	return fmt.Errorf("cannot unmarshal %T into StringOrStringSlice", v)
}

func (r Config) SourceConfigType() string {
	// Returns BigQuery source type
	return SourceType
}

func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
	if r.WriteMode == "" {
		r.WriteMode = WriteModeAllowed
		if r.ReadOnly != nil && *r.ReadOnly {
			r.WriteMode = WriteModeBlocked
		}
	}

	if r.WriteMode != WriteModeAllowed && r.WriteMode != WriteModeBlocked && r.WriteMode != WriteModeProtected {
		return nil, fmt.Errorf("invalid writeMode %q: must be one of %q, %q, or %q", r.WriteMode, WriteModeAllowed, WriteModeProtected, WriteModeBlocked)
	}

	if r.ReadOnly != nil {
		// A writeMode is considered a read-only mode if it is Blocked or Protected.
		isReadOnlyMode := (r.WriteMode == WriteModeBlocked || r.WriteMode == WriteModeProtected)

		// The declared readOnly boolean must match the writeMode's behavior.
		if *r.ReadOnly != isReadOnlyMode {
			return nil, fmt.Errorf("conflicting source configuration: readOnly is %v, but writeMode is %q", *r.ReadOnly, r.WriteMode)
		}
	}

	if r.MaxQueryResultRows == 0 {
		r.MaxQueryResultRows = 50
	}

	if r.WriteMode == WriteModeProtected && strings.ToLower(r.UseClientOAuth) != "false" && r.UseClientOAuth != "" {
		// The protected mode only allows write operations to the session's temporary datasets.

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Set writeMode to one of: allowed, blocked, protected (lowercase, exact).
  2. Remove writeMode entirely to get the default behavior, optionally using readOnly instead.
  3. If intent is read-only, set readOnly: true and drop writeMode; it will be set to blocked automatically.

Example fix

// before
writeMode: readonly
// after
writeMode: protected
Defensive patterns

Strategy: validation

Validate before calling

var validWriteModes = map[string]bool{"allowed": true, "blocked": true, "protected": true}
func validateWriteMode(mode string) error {
	if !validWriteModes[mode] {
		return fmt.Errorf("writeMode %q must be allowed|blocked|protected", mode)
	}
	return nil
}

Type guard

func isValidWriteMode(s string) bool {
	return s == "allowed" || s == "blocked" || s == "protected"
}

Try / catch

src, err := sourceRegistry.Initialize(ctx, cfg)
if err != nil {
	if strings.Contains(err.Error(), "invalid writeMode") {
		return fmt.Errorf("fix writeMode in config: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Setting writeMode in the BigQuery source config to any value other than exactly 'allowed', 'blocked', or 'protected' (note: a readOnly: true config auto-sets blocked, so this fires only when writeMode is explicitly invalid).

Common situations: Typos like 'write', 'read-write', 'Allow' (case-sensitive), or copying a writeMode value from a different source (e.g. Postgres) that uses different mode names.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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