Description
By the end of June 2026, the Confluent Schema Registry will make Avro 1.12 validation available for newly registered schemas, including stricter checks for namespaces and field default values.
Goals:
- Improve schema stability and correctness.
- Align behavior with the current Avro specification.
When the stricter validation is enabled, the change affects only new or evolved schemas. Existing stored schemas remain valid even if they don’t meet the new rules.
These validations will be disabled by default in Confluent Cloud, and can be enabled through the Schema Registry API if you want to use the Avro 1.12 strict enforcement behavior.
The validations are enabled by default in the Confluent Platform Schema Registry, starting with version 8.0.3.
The script attached to this page (also included in line at the end of this page) is a helper tool developed by Confluent Support to identify Avro subjects and namespaces that are likely to fail the stricter Avro 1.12 validations. While it has performed well in our internal testing, it is a new script that has not been formally validated to cover all possible edge cases. The results should be treated as guidance rather than a definitive compliance guarantee, and you should review the script output and underlying schemas before making any production changes.
Applies To
Confluent Cloud Schema Registry, starting end of June 2026.
Confluent Platform Schema Registry v8.0.3+
What’s Changing
1. Name and Namespace validation
Avro 1.12 namespaces and names must follow the Avro spec rules:
- The name portion of a full name, record field names, and enum symbols must:
- Start with:
A–Z,a–z, or_ - Then contain only:
A–Z,a–z,0–9, or_
- Start with:
- A namespace is a dot‑separated sequence of such names (e.g.
com.example.order). An empty string is also allowed (null namespace).
The following are no longer allowed in namespaces for new schemas:
- Spaces
-
Leading digits. For example,
1example.foo -
Reserved keywords. Metadata properties that start with
avro.such asavro.schemaoravro.codec -
Special characters, specifically
$and-
Allowed example
"namespace": "com.org1.sub_name"
This is valid (letters, digits, underscores, periods only).
Disallowed examples
"namespace": "com.org$sub" // contains '$' "namespace": "com-org.sub" // contains '-' "namespace": "1org.sub" // starts with digit "namespace": "com org.sub" // contains space
All of these will fail validation once the new behavior is enabled.
2. Default value validation
Avro 1.12 also enforces that field default values are correct for the field type:
- A field’s
defaultvalue must be a valid for that field’s schema type.
- For unions, the default must be valid for any branch of the union.
Incorrect example (will now fail)
{
"type": "record",
"name": "InvalidDefaultsTest",
"namespace": "example.avro",
"fields": [
{
"name": "badInt",
"type": "int",
"default": "test"
},
{
"name": "badArray",
"type": {
"type": "array",
"items": "string"
},
"default": "not-an-array"
}
]
}Here the defaults are invalid because:
-
badInthas typeint, but the default is a JSON string"test". The spec requires an integer value (e.g.,0,123). -
badArrayhas type array of string, but the default is a JSON string"not-an-array". The spec requires a JSON array for array defaults (e.g.,[]or["a","b"]).
3. New Confluent Platform Schema Registry Configs
The following configurations apply only to the Confluent Platform Schema Registry. They can be used to toggle the validations:
schema.validate.fields
-
Key:
schema.validate.fields -
Default:
false - Doc: “Determines whether field validation is enabled or not. If enabled, it checks whether any top level fields conflict with the reserved fields in metadata. It also checks for the presence of any field names beginning with $$.”
schema.validate.new.schemas
-
Key:
schema.validate.new.schemas -
Default:
true - Doc: “Determines whether validation for new schemas is enabled or not. If enabled, it validates both namespaces and defaults in Avro.”
Impact of enabling stricter validation
Existing schemas will continue to work as they do today, even if they contain namespaces or field defaults that are now considered invalid.
If a Schema Registry client attempts to create a new schema or evolve an existing schema with:
- An invalid namespace (per new rules), or
- An invalid field default value,
Then the Schema Registry returns a validation error and does not register the schema.
Producers may be caught in retry loops once the new validation is active because their schemas is repeatedly rejected from being registered.
Identifying Invalid Schemas
- Export schemas from Schema Registry (via API).
- Get all Subjects:
Get /subjects - Get all versions for a Subject:
Get /subjects/{subjects}/versions - Get schema for a Subject version
Get /subjects/{subjects}/versions/{versions}
- Get all Subjects:
- Run custom checks against namespaces and defaults.
- Use Avro 1.12 validation tools locally to confirm schema compatibility before registering.
How to Opt‑out of stricter validation on Confluent Platform
Confluent Platform v8.0.3+ installations can:
Modify the
schema.validate.fieldsandschema.validate.new.schemasconfigurations in the Schema Registry properties file and restart the application for the new values to take effect.Use the PUT /config endpoint on the Confluent Platform Schema Registry API to modify these configurations at runtime. Note that if you restart the Schema Registry, the runtime configurations will revert to the values defined in the properties file on startup.
curl -X PUT \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{ "validateNewSchemas": "true" }' \
${SR\_URL}/configHow to Opt‑in to stricter validation on Confluent Cloud
The stricter Avro 1.12 validation can be enabled on Confluent Cloud by calling the Schema Registry's
PUT /config API, as shown above.
References:
- Avro 1.12.0 Specification
- Confluent Docs: Strict namespace validation with Avro version 1.12
- Confluent Schema Registry Github: Upgrade to Avro 1.12.1 Pull Request #3953
In-line Avro Namespace Validation:
Content equivalent to the avro-namespace-default-field-check.sh script is provided below as an alternative way to obtain the script without downloading a file.
â–¶ View script source
#!/usr/bin/env bash
# This script audits all Avro schemas in a Confluent Cloud Schema Registry for compatibility with
# the stricter Avro 1.12 validation that will be enabled in Confluent Cloud Schema Registry
# at the end of March 2026. Avro 1.12 introduces enforcement of:
# - Valid Avro namespaces (names must start with [A-Za-z_], contain only [A-Za-z0-9_],
# and namespaces must be dot-separated sequences of such names), and
# - Correct field default values, where the JSON type of each default must match the
# field’s Avro type (for unions, the default must be valid for at least one branch,
# per the Avro 1.12 spec).
#
# The script connects to a Schema Registry instance (SR_URL, SR_API_KEY, SR_API_SECRET),
# iterates over all subjects and all of their versions, parses each Avro schema, and
# reports any versions that would be rejected once validation is enabled:
# - bad_namespace – namespace violates Avro 1.12 naming rules
# - bad_default – field default JSON type is incompatible with its declared type
# - schema_parse_error – schema payload cannot be parsed as valid JSON Avro by the script and requires manual review.
#
# Use this tool to proactively identify and clean up problematic schemas before Avro 1.12
# validation is turned on, so that future schema registrations and evolutions do not start failing unexpectedly.
#
# Run it with:
# SR_URL=... SR_API_KEY=... SR_API_SECRET=... ./check-avro-namespaces-and-defaults.sh [-v]
# and inspect the printed list of offending subject versions for remediation.
set -uo pipefail
# ---- CONFIGURE THESE ----
SR_URL="${SR_URL:-https://your-schema-registry-endpoint}"
SR_API_KEY="${SR_API_KEY:-}"
SR_API_SECRET="${SR_API_SECRET:-}"
# -------------------------
# ---- FLAGS ----
VERBOSE=0
if [[ "${1-}" == "-v" || "${1-}" == "--verbose" ]]; then
VERBOSE=1
shift
fi
info() {
echo "INFO: $@" &2
}
vlog() {
if [[ "$VERBOSE" -eq 1 ]]; then
echo "DEBUG: $@" &2
fi
}
AUTH_ARGS=()
if [[ -n "$SR_API_KEY" && -n "$SR_API_SECRET" ]]; then
AUTH_ARGS=(-u "$SR_API_KEY:$SR_API_SECRET")
fi
vlog "Using Schema Registry URL: $SR_URL"
# ---------- Helper: fetch URL and split body/status ----------
fetch() {
local url="$1"
local resp
if ! resp=$(curl -sS -w '\n%{http_code}' "${AUTH_ARGS[@]}" "$url" 2&1); then
echo "CURL_ERROR"
echo "$resp"
return 0
fi
local status body
status=$(printf '%s\n' "$resp" | tail -n1)
body=$(printf '%s\n' "$resp" | sed '$d')
echo "$status"
echo "$body"
}
# ---------- 1) List subjects ----------
subjects_resp=$(fetch "$SR_URL/subjects")
subjects_status=$(printf '%s\n' "$subjects_resp" | head -n1)
subjects_body=$(printf '%s\n' "$subjects_resp" | sed '1d')
if [[ "$subjects_status" == "CURL_ERROR" ]]; then
echo "ERROR: Failed to reach Schema Registry at $SR_URL" &2
echo "Detail: $subjects_body" &2
exit 1
fi
if [[ "$subjects_status" == "401" ]]; then
echo "ERROR: 401 Unauthorized when listing subjects from Schema Registry." &2
echo "Check SR_URL/SR_API_KEY/SR_API_SECRET (or API key/secret) and try again." &2
exit 1
fi
if [[ "$subjects_status" != "200" ]]; then
echo "ERROR: HTTP $subjects_status when listing subjects from Schema Registry." &2
echo "Response body:" &2
echo "$subjects_body" &2
exit 1
fi
if ! printf '%s\n' "$subjects_body" | jq -e . /dev/null 2&1; then
echo "ERROR: /subjects returned non-JSON body:" &2
echo "$subjects_body" &2
exit 1
fi
total_subjects=$(printf '%s\n' "$subjects_body" | jq 'length')
info "Found $total_subjects subjects from Schema Registry"
# Materialize subjects into an array
subjects=()
while IFS= read -r subject; do
subjects+=("$subject")
done <><><"$(printf 0="0" 50="=" 401="401" jq="jq" -r="-r" checked_avro_versions="0" violating_versions="0" subjects_seen="subjects_seen" violating_list="()" info="info" iterating="Iterating" through="through" all="all" subjects="subjects" and="and" versions="versions" to="to" check="check" avro="Avro" namespaces="namespaces" defaults="defaults" ----------="----------" iterate="Iterate" their="their" for="for" subject="subject" in="in" do="do" progress="Progress" every="every" if="if" then="then" checked="checked" invalid="invalid" so="so" far="far" fi="fi" list="List" the="the" versions_resp="$(fetch" versions_status="$(printf" head="head" versions_body="$(printf" sed="sed" 1d="1d" curl_error="CURL_ERROR" vlog="vlog" curl="curl" failed="failed" when="when" listing="listing" continue="continue" echo="echo" unauthorized="Unauthorized">&2
echo "Check SR_URL/SR_API_KEY/SR_API_SECRET (or API key/secret) and try again." &2
exit 1
fi
if [[ "$versions_status" != "200" ]]; then
vlog "HTTP $versions_status for subject '$subject' when calling /versions."
vlog "Response body:"
vlog "$versions_body"
continue
fi
if ! printf '%s\n' "$versions_body" | jq -e . /dev/null 2&1; then
vlog "Skipping subject '$subject' (non-JSON versions list from SR)."
vlog "Body:"
vlog "$versions_body"
continue
fi
versions=()
while IFS= read -r ver; do
versions+=("$ver")
done <><><"$(printf 401="401" jq="jq" -r="-r" iterate="Iterate" all="all" versions="versions" for="for" this="this" subject="subject" version="version" in="in" do="do" meta_resp="$(fetch" subjects="subjects" meta_status="$(printf" head="head" meta_body="$(printf" sed="sed" 1d="1d" if="if" curl_error="CURL_ERROR" then="then" vlog="vlog" curl="curl" failed="failed" when="when" fetching="fetching" continue="continue" fi="fi" echo="echo" unauthorized="Unauthorized">&2
echo "Check SR_URL/SR_API_KEY/SR_API_SECRET (or API key/secret) and try again." &2
exit 1
fi
if [[ "$meta_status" != "200" ]]; then
vlog "HTTP $meta_status for subject '$subject' version '$version' when calling /versions/{version}."
vlog "Response body:"
vlog "$meta_body"
continue
fi
if ! printf '%s\n' "$meta_body" | jq -e . /dev/null 2&1; then
vlog "Skipping subject '$subject' version '$version' (non-JSON metadata from SR)."
vlog "Body:"
vlog "$meta_body"
continue
fi
schema_type=$(printf '%s\n' "$meta_body" | jq -r '.schemaType // "AVRO"')
if [[ "$schema_type" != "AVRO" ]]; then
continue
fi
((checked_avro_versions++))
# Parse embedded schema JSON directly from meta_body
schema_json=$(printf '%s\n' "$meta_body" | jq -c '.schema | fromjson?' 2/dev/null)
if [[ -z "$schema_json" || "$schema_json" == "null" ]]; then
((violating_versions++))
reason="schema_parse_error"
summary="$subject | version=$version | reason=$reason"
violating_list+=("$summary")
echo "$summary"
vlog "Schema parse error for subject '$subject' version '$version'; treating as invalid"
continue
fi
report=$(jq -r '
def is_bad_ns:
(split(".")
| map(
(test("^[A-Za-z_][A-Za-z0-9_]*$") | not)
)
| any);
# Check whether a default value is compatible with a given Avro schema node.
def is_default_ok_for_schema($schema; $d):
if ($schema | type) == "string" then
# Primitive or named type
if $schema == "null" then ($d | type) == "null"
elif $schema == "boolean" then ($d | type) == "boolean"
elif ($schema == "int" or $schema == "long") then
($d | type) == "number" and ($d | floor) == $d
elif ($schema == "float" or $schema == "double") then
($d | type) == "number"
elif $schema == "string" then ($d | type) == "string"
elif $schema == "bytes" then ($d | type) == "string"
else
# Named record/enum/fixed (referenced by name) – skip strict checking
true
end
elif ($schema | type) == "object" then
# Inline complex types
if $schema.type == "array" then ($d | type) == "array"
elif $schema.type == "map" then ($d | type) == "object"
elif $schema.type == "record" then ($d | type) == "object"
elif $schema.type == "fixed" then ($d | type) == "string"
elif $schema.type == "enum" then ($d | type) == "string"
else
true
end
elif ($schema | type) == "array" then
# Union: default must match at least one branch (Avro 1.12)
any($schema[]; is_default_ok_for_schema(.; $d))
else
# Fallback: don’t flag
true
end;
def bad_defaults:
..
| objects
| select(has("fields"))
| .fields[]
| select(has("default"))
| {name, ftype: .type, fdefault: .default}
| select( is_default_ok_for_schema(.ftype; .fdefault) | not );
{
bad_namespaces: [ .. | objects | .namespace? // empty | select(is_bad_ns) ] | unique,
bad_defaults: [ bad_defaults ]
}
' <><><"$schema_json") ns_count="$(jq" length="length"><><><"$report") def_count="$(jq" length="length"><><><"$report") if="if" ns_count="ns_count"> 0 || def_count 0 )); then
((violating_versions++))
reason=""
if (( ns_count 0 )); then
reason="bad_namespace"
fi
if (( def_count 0 )); then
if [[ -n "$reason" ]]; then
reason+=",bad_default"
else
reason="bad_default"
fi
fi
# Collect names of fields with bad defaults (may be multiple)
bad_default_fields=""
if (( def_count 0 )); then
bad_default_fields=$(jq -r '.bad_defaults[].name' <><><"$report" 2="2">/dev/null | sort -u | paste -sd',' -)
fi
summary="subject=$subject | version=$version | reason=$reason"
if (( def_count 0 )) && [[ -n "$bad_default_fields" ]]; then
summary+=" | bad_default_fields=$bad_default_fields"
fi
violating_list+=("$summary")
# Immediately output subject name, version, reason, and fields (STDOUT)
echo "$summary"
# If verbose, also show details
if [[ "$VERBOSE" -eq 1 ]]; then
if (( ns_count 0 )); then
echo " Namespaces violating Avro naming rules:"
jq -r '.bad_namespaces[] | " - (. )"' <><><"$report" fi="fi" if="if" def_count="def_count"> 0 )); then
echo " Fields with potentially invalid Avro default values (heuristic):"
jq -r '.bad_defaults[] |
" - field: (.name) | type: (.ftype|@json) | default: (.fdefault|@json)"' \
<><><"$report" fi="fi" echo="echo" done="done" info="info" checked="Checked" avro="Avro" schema="schema" versions="versions" have="have" potential="potential" namespace="namespace" default="default" issues="issues" if="if" violating_versions="violating_versions"> 0 )); then
info "Full list of violating subject versions (with reasons):"
for entry in "${violating_list[@]}"; do
echo "INFO: $entry" &2
done
else
info "No violating schema versions found"
fi