temporalio/temporal · error

duplicate connection attr: %v:%v, %v:%v

Error message

duplicate connection attr: %v:%v, %v:%v

What it means

Returned by buildDSNAttr when a user-supplied ConnectAttributes key duplicates a parameter that is already present in the DSN built from the sqlite config (or another conflicting source). Allowing duplicates would create ambiguous URI query parameters, so the builder rejects it explicitly, listing both conflicting values.

Source

Thrown at common/persistence/sql/sqlplugin/sqlite/plugin.go:192

		"file:%s?%v",
		cfg.DatabaseName,
		vals.Encode(),
	)
	return dsn, nil
}

func buildDSNAttr(cfg *config.SQL) (url.Values, error) {
	parameters := url.Values{}

	// sort ConnectAttributes to get a deterministic order
	keys := expmaps.Keys(cfg.ConnectAttributes)
	sort.Strings(keys)

	for _, k := range keys {
		key := strings.TrimSpace(k)
		value := strings.TrimSpace(cfg.ConnectAttributes[k])
		if parameters.Get(key) != "" {
			return nil, fmt.Errorf("duplicate connection attr: %v:%v, %v:%v",
				key,
				parameters.Get(key),
				key, value,
			)
		}

		if _, isValidQueryParameter := queryParameters[key]; isValidQueryParameter {
			parameters.Set(key, value)
			continue
		}

		// assume pragma
		parameters.Add("_pragma", fmt.Sprintf("%s=%s", key, value))
	}
	// set time format
	parameters.Add("_time_format", "sqlite")
	return parameters, nil
}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Remove the duplicate key from cfg.ConnectAttributes — keep only the plugin-provided value.
  2. If you must override, check buildDSN to see which parameters are pre-populated and choose non-conflicting ones.
  3. Deduplicate keys in your config template/pipeline before it reaches the plugin.
  4. Review the error message: it shows both the existing value and your conflicting value.

Example fix

// before
connectAttributes: {"_txlock": "immediate", "cache": "shared", "_txlock": "exclusive"}
// after
connectAttributes: {"cache": "shared"}
Defensive patterns

Strategy: validation

Validate before calling

// Go: dedupe and check against plugin-provided params
reserved := map[string]bool{"mode": true, "cache": true, "_txlock": true}
for k := range cfg.ConnectAttributes {
    if reserved[strings.TrimSpace(k)] {
        return fmt.Errorf("connect attribute %q conflicts with DSN defaults", k)
    }
}

Try / catch

// Go
if err != nil && strings.Contains(err.Error(), "duplicate connection attr") {
    return fmt.Errorf("remove conflicting sqlite ConnectAttributes: %w", err)
}

Prevention

When it happens

Trigger: Passing a ConnectAttributes entry whose key collides with parameters already added to the sqlite DSN (e.g. specifying 'mode' or 'cache' in ConnectAttributes when the DSN path/query already encodes it), when calling the sqlite plugin's connection creation path.

Common situations: Copy-pasting MySQL/Postgres connect attributes into a sqlite config; explicitly setting _txlock, mode, or cache that the plugin already sets; config templating accidentally inserting the same key twice.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/8d92f300d08806c1. Report an issue: GitHub.