risingwavelabs/risingwave · error · SecretError
decode utf8 error: {0}
Error message
decode utf8 error: {0} What it means
SecretError::DecodeUtf8Error wraps std::string::FromUtf8Error and is raised when secret bytes retrieved from storage are not valid UTF-8. Secret values are handled as Rust Strings internally, so non-UTF-8 bytes (e.g. binary data stored directly as the secret) cannot be decoded.
Source
Thrown at src/common/secret/src/error.rs:28
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub use anyhow::anyhow;
use thiserror::Error;
use thiserror_ext::Construct;
use super::SecretId;
pub type SecretResult<T> = Result<T, SecretError>;
#[derive(Error, Debug, Construct)]
pub enum SecretError {
#[error("secret not found: {0}")]
ItemNotFound(SecretId),
#[error("decode utf8 error: {0}")]
DecodeUtf8Error(#[from] std::string::FromUtf8Error),
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("unspecified secret ref type: {0}")]
UnspecifiedRefType(SecretId),
#[error("failed to encrypt or decrypt the secret")]
AesError,
#[error("ser/de proto message error: {0}")]
ProtoError(#[from] bincode::Error),
#[error(transparent)]
Internal(#[from] anyhow::Error),
}
View on GitHub (pinned to 6469eb736d)
Solutions
- Store textual/base64-encoded content in the secret instead of raw binary bytes.
- Base64-encode binary material before storing, and decode it after retrieval.
- Inspect the secret value's encoding; re-create the secret with UTF-8 content.
- Fix the upstream secret provider so it returns UTF-8 encoded values.
Example fix
// before let bytes: Vec<u8> = load_der_key(); manager.create(id, bytes)?; // fails on read: not UTF-8 // after use base64::Engine; let encoded = base64::engine::general_purpose::STANDARD.encode(load_der_key()); manager.create(id, encoded)?; // store base64 text; decode after read
Defensive patterns
Strategy: validation
Validate before calling
// Validate secret bytes are UTF-8 before storing:
fn ensure_utf8(bytes: &[u8]) -> Result<&str, std::str::Utf8Error> {
std::str::from_utf8(bytes)
} Type guard
fn is_utf8_secret(bytes: &[u8]) -> bool {
std::str::from_utf8(bytes).is_ok()
} Try / catch
let secret = match manager.get(id).await {
Ok(s) => s,
Err(SecretError::DecodeUtf8Error(e)) => {
// re-create the secret with base64-encoded content
return Err(anyhow!("secret bytes not UTF-8 ({e}); re-create with base64"));
}
Err(e) => return Err(e.into()),
}; Prevention
- Store only text (or base64-encoded binary) as secret values.
- Base64-encode DER/certificate binaries before storing them as secrets.
- Verify the external secret provider returns UTF-8 encoded values.
- Decode base64 back to bytes only after reading, in application code.
When it happens
Trigger: Storing raw binary (non-UTF-8) bytes as a secret value and then reading it back via the secret manager, where conversion `String::from_utf8` fails and is converted with #[from] into SecretError::DecodeUtf8Error.
Common situations: Writing binary keys or certificates (DER, PKCS#12) directly as secret content instead of PEM/base64; a misconfigured external secret backend returning bytes in an unexpected encoding (e.g. UTF-16); corrupted secret entries in the backing store.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- invalid UTF-8 in `{key}` header
- InvalidInput
- secret not found: {0}
- I/O error: {0}
- unspecified secret ref type: {0}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/2b1e4960ea308953.
Report an issue: GitHub.