dagger/dagger · error

invalid JSON string

Error message

invalid JSON string

What it means

ID.UnmarshalJSON rejects any JSON payload that is not a quoted string (minimum 2 chars, starting and ending with a double quote). The library throws this because a Dagger ID must be a base64-encoded DAG proto serialized as a JSON string; non-string JSON (numbers, objects, null, or empty input) cannot be decoded.

Source

Thrown at dagql/call/id.go:591

	proto, err := proto.MarshalOptions{Deterministic: true}.MarshalAppend(buf, dagPB)
	if err != nil {
		return "", fmt.Errorf("failed to marshal ID proto: %w", err)
	}

	return base64.StdEncoding.EncodeToString(proto), nil
}

func (id *ID) MarshalJSON() ([]byte, error) {
	enc, err := id.Encode()
	if err != nil {
		return nil, err
	}
	return []byte(`"` + enc + `"`), nil
}

func (id *ID) UnmarshalJSON(data []byte) error {
	if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
		return fmt.Errorf("invalid JSON string")
	}
	enc := string(data[1 : len(data)-1])
	return id.Decode(enc)
}

// NOTE: use with caution, any mutations to the returned proto can corrupt the ID
func (id *ID) ToProto() (*callpbv1.DAG, error) {
	if id == nil {
		return &callpbv1.DAG{}, nil
	}
	if id.mode == idModeHandle {
		if id.typ == nil {
			return nil, fmt.Errorf("handle-form ID missing type")
		}
		return &callpbv1.DAG{
			Value: &callpbv1.DAG_EngineResult{
				EngineResult: &callpbv1.EngineResultRef{
					ResultID: id.engineResultID,

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Ensure the JSON value for the ID field is a double-quoted base64 string, e.g. "dAG..."
  2. Coerce or guard nulls before unmarshaling: only call json.Unmarshal on payloads where the ID field is a string
  3. If the value may be absent, use a *ID (pointer) or json.RawMessage and unmarshal conditionally
  4. Verify the producer is serializing the ID via ID.MarshalJSON (which quotes it) and not writing raw bytes

Example fix

// before
data := []byte(`{"id": null}`)
var v struct{ ID dagql.ID }
json.Unmarshal(data, &v) // error: invalid JSON string

// after
data := []byte(`{"id": "<base64-id>"}`)
var v struct{ ID dagql.ID }
if err := json.Unmarshal(data, &v); err != nil { /* handle */ }
Defensive patterns

Strategy: validation

Validate before calling

func validJSONStringID(b []byte) bool {
	return len(b) >= 2 && b[0] == '"' && b[len(b)-1] == '"'
}

Type guard

func isQuotedJSON(s string) bool {
	var str string
	return json.Unmarshal([]byte(s), &str) == nil
}

Try / catch

var id dagql.ID
if err := json.Unmarshal(data, &id); err != nil {
	if strings.Contains(err.Error(), "invalid JSON string") {
		// payload's id field is not a string; handle fallback
	}
	return err
}

Prevention

When it happens

Trigger: Unmarshaling JSON into dagql/call.ID when the JSON field is not a quoted string — e.g. null, a number, an object/array, or a zero-length/one-char buffer.

Common situations: Feeding hand-crafted or partially serialized JSON into an ID field; a GraphQL/server layer sending null instead of a string ID; tests or clients constructing IDs incorrectly; schema drift where an ID field became nullable.

Understand the failure class

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/bec11dd8a44a8abc. Report an issue: GitHub.