alibaba/spring-ai-alibaba · error · RuntimeException
Failed to parse store key
Error message
Failed to parse store key: {storeKey} What it means
parseStoreKey decodes a Base64 store key back into {namespace, key}; if decoding or JSON parsing fails it throws RuntimeException("Failed to parse store key: <key>"). A malformed, corrupted, or non-library-generated storeKey cannot be round-tripped, so the operation aborts with the offending key in the message.
Solutions
- Verify the storeKey is intact, valid Base64, and was produced by createStoreKey (decode it manually to check).
- Re-derive the key from namespace+key with createStoreKey instead of reusing cached/hand-copied values.
- Catch RuntimeException around parseStoreKey and treat invalid keys as not-found; log the full key for diagnosis.
- If keys came from an older library version, migrate/re-encode stored keys with the current createStoreKey.
Example fix
// before
Object[] nsKey = store.parseStoreKey(storedKey);
// after
try {
Object[] nsKey = store.parseStoreKey(storedKey.trim());
} catch (RuntimeException e) {
log.warn("Corrupt store key, rebuilding", e);
String rebuilt = store.createStoreKey(expectedNamespace, expectedKey);
Object[] nsKey = store.parseStoreKey(rebuilt);
} Defensive patterns
Strategy: try-catch
Validate before calling
boolean looksLikeStoreKey(String k) {
if (k == null || k.isBlank()) return false;
try { java.util.Base64.getDecoder().decode(k); return true; }
catch (IllegalArgumentException e) { return false; }
} Type guard
boolean isValidStoreKey(String k) {
if (k == null || k.isBlank()) return false;
try { java.util.Base64.getDecoder().decode(k); return true; }
catch (IllegalArgumentException e) { return false; }
} Try / catch
try {
Object[] nsKey = store.parseStoreKey(storeKey.trim());
} catch (RuntimeException e) {
log.warn("Unparseable store key: {}", e.getMessage());
throw new ResourceNotFoundException("store key invalid");
} Prevention
- Never hand-edit or truncate store keys copied from logs.
- Always regenerate keys via createStoreKey from namespace+key.
- During library upgrades, migrate stored keys rather than reusing old encodings.
When it happens
Trigger: Passing a storeKey that was truncated, hand-edited, generated by a different key scheme/version, or not valid Base64/JSON into APIs that accept a storeKey (e.g. reading by stored key reference).
Common situations: Migrating data between store implementations with different key formats; copying keys from logs with clipping/whitespace; upgrading the library after the key encoding changed; manually constructing keys.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to create store key
- item cannot be null
- key cannot be empty
- key cannot be null
- key cannot be null or empty
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/7942a575848eba97.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/store/stores/BaseStore.java:148
}
}
/**
* Parse store key to namespace and key.
* @param storeKey store key
* @return array containing [namespace, key]
*/
@SuppressWarnings("unchecked")
protected Object[] parseStoreKey(String storeKey) {
try {
byte[] decoded = Base64.getDecoder().decode(storeKey);
Map<String, Object> keyData = new ObjectMapper().readValue(decoded, Map.class);
List<String> namespace = (List<String>) keyData.get("namespace");
String key = (String) keyData.get("key");
return new Object[] { namespace, key };
}
catch (Exception e) {
throw new RuntimeException("Failed to parse store key: " + storeKey, e);
}
}
/**
* Check if namespace starts with given prefix.
* @param namespace namespace to check
* @param prefix prefix to match
* @return true if starts with prefix
*/
protected boolean startsWithPrefix(List<String> namespace, List<String> prefix) {
if (prefix.isEmpty()) {
return true;
}
if (prefix.size() > namespace.size()) {
return false;
}
for (int i = 0; i < prefix.size(); i++) {
if (!Objects.equals(namespace.get(i), prefix.get(i))) {View on GitHub (pinned to f82da0b50f)