Typescript SDK: updateConcurrentRecord failing validatoin

They key I’m attempting to update doesn’t already exist, but the API docs make it look as if though the operation should create or update and therefore that shouldn’t be an issue: AccelByte API Explorer | AccelByte Documentation

        const sdk = AccelbyteSessionManager.instance().getSdk();
        const args = await AccelbyteSessionManager.instance().getServerAccessArgsAsync();
        await Cloudsave.AdminConcurrentRecordAdminApi(sdk, args)
            .updateConcurrentRecord_ByUserId_ByKey(
                this.m_params.userId,
                this.getMinionCountKey(),
                {
                    set_by: setBy,
                    updatedAt,
                    value: { value: minionCount }
                });
SDK EVENT: ERROR -> {
  status: 400,
  statusText: 'Bad Request',
  headers: AxiosHeaders {
    date: 'Fri, 09 Feb 2024 15:41:23 GMT',
    'content-type': 'application/json',
    'content-length': '103',
    connection: 'close',
    'x-ab-traceid': 'ed2ccf86c0494b48ac72805fdc09afae',
    'x-envoy-upstream-service-time': '1',
    server: 'envoy'
  },
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [ 'xhr', 'http' ],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 60000,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function], Blob: [class Blob] },
    validateStatus: [Function: validateStatus],
    headers: AxiosHeaders {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/json',
      Authorization: 'Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjY5YjMzMmY0ZDg5M2Q0ZWFlZTc1NjFlM2ZjM2IyMzliODk5MTM5YzMiLCJ0eXAiOiJKV1QifQ.eyJiYW5zIjpudWxsLCJjbGllbnRfaWQiOiI0MTM4ZTA2NGI2YzQ0ZTNhYmYzOWYxMWY1MTA0N2Y4MiIsImNvdW50cnkiOiIiLCJkaXNwbGF5X25hbWUiOiIiLCJleHAiOjE3MDc0OTY4MzcsImlhdCI6MTcwNzQ5MzIzNywiaXNfY29tcGx5Ijp0cnVlLCJpc3MiOiJodHRwczovL2Rldi5nYW1pbmdzZXJ2aWNlcy5hY2NlbGJ5dGUuaW8iLCJqZmxncyI6MCwibmFtZXNwYWNlIjoiUlBNIiwibmFtZXNwYWNlX3JvbGVzIjpudWxsLCJwYXJlbnRfbmFtZXNwYWNlIjoidXBzaG90IiwicGVybWlzc2lvbnMiOltdLCJyb2xlcyI6WyJmMzQ5NjNkOWE3NjM0MDQzODk4ZmJmOWNkNGZkNzVmNiJdLCJzY29wZSI6ImFjY291bnQgY29tbWVyY2Ugc29jaWFsIHB1Ymxpc2hpbmcgYW5hbHl0aWNzIn0.DxOWNsu0MZHNfwpxKdGGSKpXOcfslL5_Mrqiqjghs7Idas6fvcWDtTDvJOnuHOBhZlFVqhAKJnSOgXIhC7J9478a175k-VqUlsvYo7_sNsnoAXWR_q3fUTLHgh7R56dRszo1_iuz7cR4HMys6U0EE0fv27h27VN1poSCwBrxM3qN6meT0RFGSWeh1X9bcDVVM-2v7AWQotzhQe1bDfIzsFiZzgMhLHd3vbRrUIgLCqIicEy76Wkv63oxNiouC51L1BqJJJ3AdyZzLV6r8hjpLNcD_Yc3dV1FH2yBKfsKKnx0JKPJyA_mYASeljki0XQ7qQTlLID-51WAKUAwJdxStA',
      'User-Agent': 'axios/1.3.6',
      'Content-Length': '50',
      'Accept-Encoding': 'gzip, compress, deflate, br'
    },
    paramsSerializer: { serialize: [Function (anonymous)] },
    baseURL: 'https://__redacted__.dev.gamingservices.accelbyte.io',
    withCredentials: true,
    params: {},
    method: 'put',
    url: '/cloudsave/v1/admin/namespaces/__redacted__/users/f929a150609a45feac678f107ddd0184/concurrent/records/currency_minions',
    data: '{"set_by":"","updatedAt":"","value":{"value":150}}'
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      finish: [Function: requestOnFinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    strictContentLength: false,
    _contentLength: '50',
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: false,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: '__redacted__.dev.gamingservices.accelbyte.io',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 10,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: '__redacted__.dev.gamingservices.accelbyte.io',
      _closeAfterHandlingError: false,
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [Circular *1],
      timeout: 60000,
      [Symbol(res)]: [TLSWrap],
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 430,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: Timeout {
        _idleTimeout: 60000,
        _idlePrev: [TimersList],
        _idleNext: [TimersList],
        _idleStart: 63808,
        _onTimeout: [Function: bound ],
        _timerArgs: undefined,
        _repeat: null,
        _destroyed: false,
        [Symbol(refed)]: false,
        [Symbol(kHasPrimitive)]: false,
        [Symbol(asyncId)]: 441,
        [Symbol(triggerId)]: 433
      },
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(kSetKeepAlive)]: true,
      [Symbol(kSetKeepAliveInitialDelay)]: 60,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object]
    },
    _header: 'PUT /cloudsave/v1/admin/namespaces/__redacted__/users/f929a150609a45feac678f107ddd0184/concurrent/records/currency_minions HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: application/json\r\n' +
      'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjY5YjMzMmY0ZDg5M2Q0ZWFlZTc1NjFlM2ZjM2IyMzliODk5MTM5YzMiLCJ0eXAiOiJKV1QifQ.eyJiYW5zIjpudWxsLCJjbGllbnRfaWQiOiI0MTM4ZTA2NGI2YzQ0ZTNhYmYzOWYxMWY1MTA0N2Y4MiIsImNvdW50cnkiOiIiLCJkaXNwbGF5X25hbWUiOiIiLCJleHAiOjE3MDc0OTY4MzcsImlhdCI6MTcwNzQ5MzIzNywiaXNfY29tcGx5Ijp0cnVlLCJpc3MiOiJodHRwczovL2Rldi5nYW1pbmdzZXJ2aWNlcy5hY2NlbGJ5dGUuaW8iLCJqZmxncyI6MCwibmFtZXNwYWNlIjoiUlBNIiwibmFtZXNwYWNlX3JvbGVzIjpudWxsLCJwYXJlbnRfbmFtZXNwYWNlIjoidXBzaG90IiwicGVybWlzc2lvbnMiOltdLCJyb2xlcyI6WyJmMzQ5NjNkOWE3NjM0MDQzODk4ZmJmOWNkNGZkNzVmNiJdLCJzY29wZSI6ImFjY291bnQgY29tbWVyY2Ugc29jaWFsIHB1Ymxpc2hpbmcgYW5hbHl0aWNzIn0.DxOWNsu0MZHNfwpxKdGGSKpXOcfslL5_Mrqiqjghs7Idas6fvcWDtTDvJOnuHOBhZlFVqhAKJnSOgXIhC7J9478a175k-VqUlsvYo7_sNsnoAXWR_q3fUTLHgh7R56dRszo1_iuz7cR4HMys6U0EE0fv27h27VN1poSCwBrxM3qN6meT0RFGSWeh1X9bcDVVM-2v7AWQotzhQe1bDfIzsFiZzgMhLHd3vbRrUIgLCqIicEy76Wkv63oxNiouC51L1BqJJJ3AdyZzLV6r8hjpLNcD_Yc3dV1FH2yBKfsKKnx0JKPJyA_mYASeljki0XQ7qQTlLID-51WAKUAwJdxStA\r\n' +
      'User-Agent: axios/1.3.6\r\n' +
      'Content-Length: 50\r\n' +
      'Accept-Encoding: gzip, compress, deflate, br\r\n' +
      'Host: __redacted__.dev.gamingservices.accelbyte.io\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'PUT',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    joinDuplicateHeaders: undefined,
    path: '/cloudsave/v1/admin/namespaces/__redacted__/users/f929a150609a45feac678f107ddd0184/concurrent/records/currency_minions',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 4,
      _maxListeners: undefined,
      socket: [TLSSocket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      joinDuplicateHeaders: undefined,
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 400,
      statusMessage: 'Bad Request',
      client: [TLSSocket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'https://__redacted__.dev.gamingservices.accelbyte.io/cloudsave/v1/admin/namespaces/__redacted__/users/f929a150609a45feac678f107ddd0184/concurrent/records/currency_minions',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 14,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: '__redacted__.dev.gamingservices.accelbyte.io',
    protocol: 'https:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 50,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'https://__redacted__.dev.gamingservices.accelbyte.io/cloudsave/v1/admin/namespaces/__redacted__/users/f929a150609a45feac678f107ddd0184/concurrent/records/currency_minions',
      _timeout: null,
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      authorization: [Array],
      'user-agent': [Array],
      'content-length': [Array],
      'accept-encoding': [Array],
      host: [Array]
    },
    [Symbol(errored)]: null,
    [Symbol(kHighWaterMark)]: 16384,
    [Symbol(kRejectNonStandardBodyWrites)]: false,
    [Symbol(kUniqueHeaders)]: null
  },
  data: {
    errorCode: 18064,
    errorMessage: 'unable to update_player_record_concurrent: validation error'
  }
}

Thanks for the detailed logs @njupshot

SDK Team is currently checking this, will get back to you asap.

Hi @njupshot,

The endpoint should be able to create the Player record when the key is not yet exist. Looking at the error details, it seems the set_by and updatedAt is not properly set making the request contain empty string.

url: '/cloudsave/v1/admin/namespaces/__redacted__/users/f929a150609a45feac678f107ddd0184/concurrent/records/currency_minions',
data: '{"set_by":"","updatedAt":"","value":{"value":150}}'

So, please help make sure that both value is correctly set before making the query.

Thanks.

Fields info
  • Set by: Indicate which party that could modify the Player record
    SERVER: record can be modified by server only.
    CLIENT: record can be modified by client and server.

  • upadtedAt (required)
    For initial request/new key, we could use current date time, but for the subsequent request updatedAt should contain the value from the latest updatedAt for the current record.

Thank you for the response. In the case that the record already exists, I have values for setBy and updatedAt from the previous fetch to pass back in. What should I set those values to in the case that the key didn’t already exist?

Sorry for being unclear, I have included the information about the fields as a spoiler at the bottom of my pervious reply. Anyway, here is the details:

  • Set by: Indicate which party that could modify the Player record
    SERVER: record can be modified by server only.
    CLIENT: record can be modified by client and server.

  • upadtedAt (required)
    For initial request/new key, we could use current date time, but for the subsequent request updatedAt should contain the value from the latest updatedAt for the current record. Also please make sure that the format of the date time is using the ISO string or RFC3339 format.

Thanks.