larksuite/cli · error

invalid JSON in %s: %w

Error message

invalid JSON in %s: %w

What it means

ReadOpenClawConfig reads an openclaw.json file and unmarshals it into the OpenClawRoot struct (channels + secrets subtree). This error is thrown when the file exists and is readable but its bytes are not valid JSON (json.Unmarshal fails). The path is embedded in the message and the underlying json.SyntaxError/json.UnmarshalTypeError is wrapped via %w, so callers can inspect the cause for offset/type details.

Source

Thrown at internal/binding/reader.go:22

package binding

import (
	"encoding/json"
	"fmt"

	"github.com/larksuite/cli/internal/vfs"
)

// ReadOpenClawConfig reads and parses an openclaw.json file at the given path.
func ReadOpenClawConfig(path string) (*OpenClawRoot, error) {
	data, err := vfs.ReadFile(path)
	if err != nil {
		return nil, err // caller (bind.go) formats user-facing message with path context
	}

	var root OpenClawRoot
	if err := json.Unmarshal(data, &root); err != nil {
		return nil, fmt.Errorf("invalid JSON in %s: %w", path, err)
	}

	return &root, nil
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Open the file named in the error and run it through a JSON linter (python3 -m json.tool <path>) to find the exact syntax error at the wrapped offset.
  2. Remove JSON-illegal constructs: comments, trailing commas, single quotes, smart quotes; encoding/json is strict.
  3. If a placeholder like {{...}} remains unsubstituted, supply the real value or an env template "${VAR}" instead.
  4. Delete the corrupt file and re-run the tool that generates openclaw.json, or restore it from backup/version control.
  5. Unwrap the cause in code (errors.As to *json.SyntaxError) to programmatically report line/column offsets.

Example fix

// before (invalid: trailing comma)
{ "channels": { "feishu": { "appId": "cli_x", "appSecret": "s" ,} } }
// after
{ "channels": { "feishu": { "appId": "cli_x", "appSecret": "s" } } }
Defensive patterns

Strategy: validation

Validate before calling

func validateOpenClawJSON(path string) error {
	data, err := os.ReadFile(path)
	if err != nil { return err }
	var v any
	if err := json.Unmarshal(data, &v); err != nil {
		var se *json.SyntaxError
		if errors.As(err, &se) {
			line := 1 + bytes.Count(data[:se.Offset], []byte("\n"))
			return fmt.Errorf("%s: invalid JSON at line %d: %v", path, line, se)
		}
		return fmt.Errorf("%s: invalid JSON: %w", path, err)
	}
	return nil
}

Type guard

func isOpenClawJSONValid(path string) bool {
	data, err := os.ReadFile(path)
	if err != nil { return false }
	var v any
	return json.Unmarshal(data, &v) == nil
}

Try / catch

root, err := binding.ReadOpenClawConfig(path)
if err != nil {
	var se *json.SyntaxError
	if errors.As(err, &se) {
		fmt.Fprintf(os.Stderr, "fix JSON syntax at offset %d in %s: %v\n", se.Offset, path, se)
	}
	return err
}

Prevention

When it happens

Trigger: Calling ReadOpenClawConfig(path) where the file at path contains malformed JSON: truncated file, trailing commas, comments, single quotes, BOM, smart quotes from copy-paste, or an appSecret given as a non-string/non-object literal (the SecretInput.UnmarshalJSON error also surfaces here as an UnmarshalTypeError-style failure).

Common situations: A text editor or heredoc script wrote a partial/corrupt openclaw.json; a template placeholder like {{SECRET}} was never substituted; the file was hand-edited with comments or trailing commas that strict encoding/json rejects; a file-sync tool wrote a zero-byte or half-written file.

Understand the failure class

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/07d99543141a2e6f. Report an issue: GitHub.