hyperledger/fabric · error

invalid key [%s], cannot begin with "_"

Error message

invalid key [%s], cannot begin with "_"

What it means

validateKey enforces CouchDB document-key rules before a key becomes a CouchDB _id. Keys starting with an underscore are rejected because CouchDB reserves the '_'-prefixed field names (like _id, _rev, _design) for its own document metadata; storing such a key would collide with that namespace.

Source

Thrown at core/ledger/kvledger/txmgmt/statedb/statecouchdb/couchdoc_conv.go:291

		return "", err
	}
	return dataformatInfo.Version, nil
}

func validateValue(value []byte) error {
	isJSON, jsonVal := tryCastingToJSON(value)
	if !isJSON {
		return nil
	}
	return jsonVal.checkReservedFieldsNotPresent()
}

func validateKey(key string) error {
	if !utf8.ValidString(key) {
		return errors.Errorf("invalid key [%x], must be a UTF-8 string", key)
	}
	if strings.HasPrefix(key, "_") {
		return errors.Errorf("invalid key [%s], cannot begin with \"_\"", key)
	}
	if key == "" {
		return errors.New("invalid key. Empty string is not supported as a key by couchdb")
	}
	return nil
}

// removeJSONRevision removes the "_rev" if this is a JSON
func removeJSONRevision(jsonValue *[]byte) error {
	jsonVal, err := castToJSON(*jsonValue)
	if err != nil {
		logger.Errorf("Failed to unmarshal couchdb JSON data: %+v", err)
		return err
	}
	jsonVal.removeRevField()
	if *jsonValue, err = jsonVal.toBytes(); err != nil {
		logger.Errorf("Failed to marshal couchdb JSON data: %+v", err)
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Rename the key so it does not start with '_' (prefix with a safe character, e.g. 'x_' or strip/replace the leading underscore).
  2. Sanitize keys at the application boundary before PutState, rejecting or remapping underscore-prefixed names.
  3. If the data came from an import/migration, re-run the migration with key transformation applied.
  4. If the key must start with '_' semantically, use LevelDB (stateleveldb) as the state database instead of CouchDB.

Example fix

// before
ctx.GetStub().PutState("_userCounter", []byte("1"))
// after
ctx.GetStub().PutState("userCounter", []byte("1"))
Defensive patterns

Strategy: validation

Validate before calling

func validStateKey(key string) error {
  if !utf8.ValidString(key) { return fmt.Errorf("key not UTF-8: %x", key) }
  if strings.HasPrefix(key, "_") { return fmt.Errorf("key must not start with '_': %s", key) }
  if key == "" { return errors.New("key must be non-empty") }
  return nil
}
// call before PutState
if err := validStateKey(key); err != nil { return err }

Type guard

func isSafeKey(key string) bool {
  return key != "" && !strings.HasPrefix(key, "_") && utf8.ValidString(key)
}

Prevention

When it happens

Trigger: Calling ValidateKeyValue with a key whose first character is '_' (e.g. a state key literally named '_config' or '_temp'), or reading a record from the DB (readFromDB) whose stored key begins with '_' — typically after a write that bypassed validation or an external import.

Common situations: Chaincode or migration tooling generating keys from JSON field names that start with '_'; importing legacy data into a CouchDB-backed ledger; keys constructed by concatenating prefixes that produce a leading underscore.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/d6bbb42aff75e8ac. Report an issue: GitHub.