thingsboard/thingsboard · error · RuntimeException
Failed to parse service account key JSON
Error message
Failed to parse service account key JSON
What it means
Thrown by Langchain4jChatModelConfigurerImpl when configuring a Google Vertex AI Gemini chat model: ServiceAccountCredentials.fromStream cannot parse the configured providerConfig().serviceAccountKey() bytes as valid Google service-account JSON. The RuntimeException aborts chat-model creation, so any AI feature relying on that model fails to start.
Source
Thrown at application/src/main/java/org/thingsboard/server/service/ai/Langchain4jChatModelConfigurerImpl.java:123
.topP(chatModelConfig.topP())
.topK(chatModelConfig.topK())
.frequencyPenalty(chatModelConfig.frequencyPenalty())
.presencePenalty(chatModelConfig.presencePenalty())
.maxOutputTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
}
@Override
public ChatModel configureChatModel(GoogleVertexAiGeminiChatModelConfig chatModelConfig) {
GoogleCredentials credentials;
try {
credentials = ServiceAccountCredentials
.fromStream(new ByteArrayInputStream(chatModelConfig.providerConfig().serviceAccountKey().getBytes(StandardCharsets.UTF_8)))
.createScoped("https://www.googleapis.com/auth/cloud-platform");
} catch (IOException e) {
throw new RuntimeException("Failed to parse service account key JSON", e);
}
return GoogleGenAiChatModel.builder()
.projectId(chatModelConfig.providerConfig().projectId())
.location(chatModelConfig.providerConfig().location())
.googleCredentials(credentials)
.modelName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.topK(chatModelConfig.topK())
.frequencyPenalty(chatModelConfig.frequencyPenalty())
.presencePenalty(chatModelConfig.presencePenalty())
.maxOutputTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
}
@OverrideView on GitHub (pinned to 45c30e83fa)
Solutions
- Re-download the JSON key file from Google Cloud IAM (Service Accounts > Keys) and paste its full, exact contents into the provider config.
- Validate the pasted value parses as JSON and contains the fields google-libraries expect (client_email, private_key, project_id) before saving.
- Ensure no env-var placeholder or base64 wrapper is left around the value.
- If using env interpolation, confirm the variable is actually set in the service environment.
Example fix
// before
serviceAccountKey: "${GCP_SERVICE_ACCOUNT_KEY}" // variable unset -> literal placeholder parsed
// after
// export GCP_SERVICE_ACCOUNT_KEY=$(cat ./sa-key.json) so interpolation yields valid JSON Defensive patterns
Strategy: validation
Validate before calling
// Validate the key parses and has required fields BEFORE configuring the model
ObjectNode node;
try {
node = (ObjectNode) JacksonUtil.fromString(serviceAccountKey, JsonNode.class);
} catch (Exception e) { throw new IllegalArgumentException("key is not valid JSON"); }
if (node.get("client_email") == null || node.get("private_key") == null || node.get("project_id") == null) {
throw new IllegalArgumentException("service account key missing client_email/private_key/project_id");
}
// only then call configureChatModel(config) Try / catch
try { model = configurer.configureChatModel(geminiConfig); }
catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("Failed to parse service account key JSON")) {
// reject config at save time, prompt user to re-upload the JSON key file
} else throw e;
} Prevention
- Validate service-account JSON (parse + required fields) in the settings UI before saving AI provider config.
- Store the key as a file reference/secret, never a hand-typed string.
When it happens
Trigger: Setting the service account key in the ThingsBoard AI configuration to malformed JSON; pasting a truncated key; passing a base64-encoded or PEM key instead of the raw Google Cloud service-account JSON file contents; an empty string.
Common situations: Copy-paste errors when moving the key from the Google Cloud console (missing closing brace, smart quotes, extra whitespace/newlines); downloading the wrong key format; environment-variable interpolation producing a placeholder like '${GCP_KEY}' that was never substituted.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- BAD_REQUEST_PARAMS
- Failed to parse alarm schedule from '{}'
- Value is not a valid JSON
- BAD_REQUEST_PARAMS
- Template is missing
AI-assisted analysis of thingsboard/thingsboard@45c30e83fa (2026-08-14).
Data as JSON: /api/errors/fb2269f42fa24953.
Report an issue: GitHub.