Description
When a local instance with Vertex AI (GCP service account) is stopped and then started, the GCP service account JSON file in the volume (/home/node/.openclaw/gcp/sa.json) is overwritten with the literal string (on-volume) instead of the actual credentials.
This causes LiteLLM to fail with:
Vertex_aiException InternalServerError - ('File /home/node/.openclaw/gcp/sa.json is not a valid json file.',
JSONDecodeError('Expecting value: line 1 column 1 (char 0)'))
Root Cause
In src/server/routes/status-instances.ts:127, the config parser sets gcpServiceAccountJson to a placeholder string when it detects the credential path was set:
gcpServiceAccountJson: savedVars.GOOGLE_APPLICATION_CREDENTIALS ? "(on-volume)" : undefined,
This is intended as a sentinel value to indicate credentials exist on the volume. However, the start() method in local.ts treats any truthy gcpServiceAccountJson as real content and writes it to the volume (lines 1819-1830), overwriting the valid SA JSON with (on-volume).
Expected Behavior
The start() method should skip writing the GCP SA file when the value is a placeholder, or parseSavedLocalInstanceConfig() should not set gcpServiceAccountJson at all since the real file already exists on the volume.
Steps to Reproduce
- Deploy a local instance with Vertex AI and a GCP service account JSON
- Verify the agent works (LiteLLM authenticates successfully)
- Stop the instance
- Start the instance
- Agent fails — LiteLLM cannot parse
sa.json
Workaround
Teardown and do a fresh deploy instead of stop/start.
This issue was filed by Claude Code under the supervision of Bill Murdock.
Description
When a local instance with Vertex AI (GCP service account) is stopped and then started, the GCP service account JSON file in the volume (
/home/node/.openclaw/gcp/sa.json) is overwritten with the literal string(on-volume)instead of the actual credentials.This causes LiteLLM to fail with:
Root Cause
In
src/server/routes/status-instances.ts:127, the config parser setsgcpServiceAccountJsonto a placeholder string when it detects the credential path was set:This is intended as a sentinel value to indicate credentials exist on the volume. However, the
start()method inlocal.tstreats any truthygcpServiceAccountJsonas real content and writes it to the volume (lines 1819-1830), overwriting the valid SA JSON with(on-volume).Expected Behavior
The
start()method should skip writing the GCP SA file when the value is a placeholder, orparseSavedLocalInstanceConfig()should not setgcpServiceAccountJsonat all since the real file already exists on the volume.Steps to Reproduce
sa.jsonWorkaround
Teardown and do a fresh deploy instead of stop/start.
This issue was filed by Claude Code under the supervision of Bill Murdock.