SigNoz/signoz · error · model.ApiError

SetTTLV2 only supported

Error message

SetTTLV2 only supported

What it means

ApiError (ErrorExec) from setTTLLogs when the logs table has a custom_retention column (hasCustomRetention == true). Legacy per-org TTL setting via this path is refused because the schema is on the newer custom-retention model where SetTTLV2 must be used instead.

Source

Thrown at pkg/query-service/app/clickhouseReader/reader.go:930

	return &response, nil
}

func getLocalTableName(tableName string) string {

	tableNameSplit := strings.Split(tableName, ".")
	return tableNameSplit[0] + "." + strings.Split(tableNameSplit[1], "distributed_")[1]

}

func (r *ClickHouseReader) setTTLLogs(ctx context.Context, orgID string, params *retentiontypes.TTLParams) (*retentiontypes.SetTTLResponseItem, *model.ApiError) {
	ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
		instrumentationtypes.TelemetrySignal:  telemetrytypes.SignalLogs.StringValue(),
		instrumentationtypes.CodeNamespace:    "clickhouse-reader",
		instrumentationtypes.CodeFunctionName: "setTTLLogs",
	})
	hasCustomRetention, err := r.hasCustomRetentionColumn(ctx)
	if hasCustomRetention {
		return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("SetTTLV2 only supported")}
	}
	if err != nil {
		return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in processing TTL")}
	}
	// uuid is used as transaction id
	uuidWithHyphen := uuid.New()
	uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)

	coldStorageDuration := -1
	if len(params.ColdStorageVolume) > 0 {
		coldStorageDuration = int(params.ToColdStorageDuration)
	}

	tableNameArray := []string{
		r.logsDB + "." + r.logsLocalTableV2,
		r.logsDB + "." + r.logsResourceLocalTableV2,
		getLocalTableName(r.logsDB + "." + r.logsAttributeKeys),
		getLocalTableName(r.logsDB + "." + r.logsResourceKeys),

View on GitHub (pinned to 5069bf80b0)

Solutions

  1. Switch the caller to the SetTTLV2 API for logs retention
  2. Upgrade any scripts/UI to the v2 endpoints
  3. If legacy behavior is truly required, consult migration docs rather than forcing v1

Example fix

// before
apiErr := reader.SetTTL(ctx, orgID, ttlParams) // legacy logs TTL

// after
apiErr := reader.SetTTLV2(ctx, orgID, v2Params) // custom-retention schema
Defensive patterns

Strategy: type-guard

Validate before calling

// Probe schema once and route to the right API
hasV2, err := schemaHasCustomRetention(ctx, ch)
if err != nil { return err }
if hasV2 { return reader.SetTTLV2(ctx, orgID, v2Params) }
return reader.SetTTL(ctx, orgID, v1Params)

Type guard

func requiresTTLV2(apiErr *model.ApiError) bool {
    return apiErr != nil && strings.Contains(apiErr.Err.Error(), "SetTTLV2 only supported")
}

Try / catch

apiErr := reader.SetTTL(ctx, orgID, params)
if requiresTTLV2(apiErr) {
    apiErr = reader.SetTTLV2(ctx, orgID, convertToV2(params))
}

Prevention

When it happens

Trigger: Invoking the logs TTL endpoint (SetTTL/setTTLLogs) against a SigNoz schema that has already migrated to custom retention — the guard clause returns immediately with 'SetTTLV2 only supported'.

Common situations: Older clients/automation still calling the v1 TTL API after the SigNoz upgrade added custom_retention; mixed versions during rolling upgrades.

Related errors


AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28). Data as JSON: /api/errors/15f5edd6658a81a7. Report an issue: GitHub.