JuliusBrussee/caveman · error

ccr: typed object session_id is required

Error message

ccr: typed object session_id is required

What it means

prepareObject (reached via PutObject) rejects any typed object whose SessionID is blank after trimming. Session identity is mandatory because every CCR object is scoped to a session for retrieval and permission checks. The error fires before any hashing or storage happens.

Source

Thrown at engine/ccr/store.go:106

	OriginalByteLength int         `json:"original_byte_length"`
	StoredByteLength   int         `json:"stored_byte_length"`
	Data               []byte      `json:"data"`
}

var objectTypes = map[ObjectType]struct{}{
	ObjectFileObservation: {}, ObjectSearchResult: {}, ObjectCommandResult: {},
	ObjectTestResult: {}, ObjectBuildResult: {}, ObjectDiffSnapshot: {},
	ObjectTaskContract: {}, ObjectTaskDecision: {}, ObjectExecutionState: {},
	ObjectDocumentationExcerpt: {}, ObjectBrowserSnapshot: {},
	ObjectRepositoryMap: {}, ObjectEvidenceBundle: {},
}

func prepareObject(obj Object) (Object, error) {
	if _, ok := objectTypes[obj.Type]; !ok {
		return Object{}, fmt.Errorf("ccr: unknown object type %q", obj.Type)
	}
	if strings.TrimSpace(obj.SessionID) == "" {
		return Object{}, errors.New("ccr: typed object session_id is required")
	}
	if obj.Currentness == "" {
		obj.Currentness = Current
	}
	if obj.Currentness != Current && obj.Currentness != Stale && obj.Currentness != Archived {
		return Object{}, fmt.Errorf("ccr: unknown currentness %q", obj.Currentness)
	}
	if obj.Lifecycle == "" {
		obj.Lifecycle = Hot
	}
	if obj.Lifecycle != Hot && obj.Lifecycle != Warm && obj.Lifecycle != Cold && obj.Lifecycle != LifecycleArchived {
		return Object{}, fmt.Errorf("ccr: unknown lifecycle %q", obj.Lifecycle)
	}
	if obj.CreatedAt.IsZero() {
		obj.CreatedAt = time.Now().UTC()
	} else {
		obj.CreatedAt = obj.CreatedAt.UTC()
	}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Set obj.SessionID to the active session identifier before PutObject
  2. If the object arrives from JSON, verify the producer emits `session_id` exactly and the struct tags match
  3. Fail fast at object construction: validate SessionID in your builder/helper so the store never sees it blank

Example fix

// before
obj := ccr.Object{Type: ccr.ObjectCommandResult, Data: data}
id, err := store.PutObject(obj)

// after
obj := ccr.Object{
    Type:      ccr.ObjectCommandResult,
    SessionID: sessionID, // required
    Data:      data,
}
id, err := store.PutObject(obj)
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(obj.SessionID) == "" {
    return errors.New("session_id required before PutObject")
}
id, err := store.PutObject(obj)

Type guard

func validSessionID(s string) bool { return strings.TrimSpace(s) != "" }

Prevention

When it happens

Trigger: Calling PutObject(Object{...}) with SessionID omitted or set to whitespace; building objects from deserialized JSON where the session_id field was missing or misnamed (e.g. sessionId vs session_id); test fixtures that only set Type and Data.

Common situations: JSON field-name mismatch between a producer using camelCase and the Go struct's `json:"session_id"` tag; refactoring code that previously derived sessionID implicitly; copy-pasted test objects missing the field.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/494eddd442d699e5. Report an issue: GitHub.