googleapis/mcp-toolbox · error

conflicting source configuration: readOnly is %v, but writeM

Error message

conflicting source configuration: readOnly is %v, but writeMode is %q

What it means

Initialize cross-checks the readOnly boolean against writeMode. A writeMode of 'blocked' or 'protected' implies read-only, and 'allowed' implies writable; if the explicit readOnly flag disagrees with what writeMode implies, initialization fails with this error rather than guessing intent.

Source

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

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.
		// when using client OAuth, a new session is created every
		// time a BigQuery tool is invoked. Therefore, no session data can
		// be preserved as needed by the protected mode.
		return nil, fmt.Errorf("writeMode 'protected' cannot be used with useClientOAuth enabled")
	}

	if strings.ToLower(r.UseClientOAuth) != "false" && r.UseClientOAuth != "" && r.ImpersonateServiceAccount != "" {
		return nil, fmt.Errorf("useClientOAuth cannot be used with impersonateServiceAccount")
	}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Make readOnly agree with writeMode: blocked/protected => readOnly: true; allowed => readOnly: false.
  2. Remove the readOnly field and express intent purely via writeMode.
  3. Remove writeMode and express intent purely via readOnly: true (auto-maps to blocked).

Example fix

// before
readOnly: true
writeMode: allowed
// after
readOnly: true
writeMode: blocked
Defensive patterns

Strategy: validation

Validate before calling

func validateReadOnlyWriteMode(readOnly *bool, writeMode string) error {
	if readOnly == nil { return nil }
	isRO := writeMode == "blocked" || writeMode == "protected"
	if *readOnly != isRO {
		return fmt.Errorf("readOnly=%v conflicts with writeMode=%q", *readOnly, writeMode)
	}
	return nil
}

Try / catch

src, err := sourceRegistry.Initialize(ctx, cfg)
if err != nil {
	if strings.Contains(err.Error(), "conflicting source configuration") {
		return fmt.Errorf("align readOnly and writeMode: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Configuring both readOnly and writeMode with contradictory values, e.g. readOnly: true with writeMode: allowed, or readOnly: false with writeMode: blocked/protected.

Common situations: Merging config fragments where one sets readOnly: true and another adds writeMode: allowed; misunderstanding that protected is a read-only mode; copy-pasting examples with both fields set.

Related errors


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