Skip to content

@createdTime directive produces null for some records #329

Description

@hdbjeff

@createdTime directive produces null for some records

Summary

@createdTime produces null for some records in a table while working correctly for others. All records are created via the same code path (tables.MyTable.post(record)). @updatedTime works correctly on the same affected records (via patch()).

Schema

type MyTable @table {
  id: ID @primaryKey
  name: String @indexed
  normalizedName: String @indexed
  externalId: String @indexed
  tier: String
  lastSyncedAt: Date
  createdAt: Date @createdTime
  updatedAt: Date @updatedTime
}

Write code

All records are created by the same function:

const existing = await findByExternalId(externalId); // uses search()

const record = {
  name: org.name,
  normalizedName: normalize(org.name),
  externalId,
  tier,
  lastSyncedAt: new Date().toISOString(),
};

if (existing) {
  await tables.MyTable.patch(existing.id, record);
} else {
  await tables.MyTable.post(record);
}

This runs on a 12-hour setInterval scheduler. On the first run (application startup), ~24 records were created via post(). On a later run, 1 additional record was created via post() when a new entry appeared in the external system. That record has createdAt: null.

Observed behavior

1. Operations API: search_by_value with get_attributes: ["*"]

Working record (created during initial startup):

[
    {
        "name": "Acme Corp",
        "normalizedName": "acme corp",
        "externalId": "12345",
        "tier": "premium",
        "lastSyncedAt": "2026-04-08T13:42:29.704Z",
        "updatedAt": "2026-04-08T13:42:29.704Z",
        "id": "f0729b11-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "createdAt": "2026-03-25T00:08:54.524Z"
    }
]

Broken record (created on a later scheduler cycle):

[
    {
        "name": "Globex Inc",
        "normalizedName": "globex inc",
        "externalId": "67890",
        "tier": "premium",
        "lastSyncedAt": "2026-04-08T13:42:29.803Z",
        "id": "7f823526-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "updatedAt": "2026-04-08T13:42:29.803Z"
    }
]

createdAt is omitted entirely from the wildcard response for the broken record.

2. Operations API: search_by_value with explicit get_attributes

Working record:

[
    {
        "name": "Acme Corp",
        "createdAt": "2026-03-25T00:08:54.524Z",
        "updatedAt": "2026-04-08T13:42:29.704Z",
        "id": "f0729b11-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }
]

Broken record:

[
    {
        "name": "Globex Inc",
        "createdAt": null,
        "updatedAt": "2026-04-08T13:42:29.803Z"
    }
]

When explicitly requested, createdAt is returned as null for the broken record.

3. Programmatic search() vs get()Object.keys() difference

Additionally, get() and search() return records with different enumerability for all records (not just broken ones):

// search() — keys are enumerable
for await (const r of tables.MyTable.search({ conditions: [...], limit: 1 })) {
  console.log(Object.keys(r));
  // ["name","normalizedName","externalId","tier","lastSyncedAt","updatedAt","id","createdAt"]
}

// get() — keys are NOT enumerable
const g = await tables.MyTable.get(id);
console.log(Object.keys(g));
// [] (empty for ALL records, working and broken)
// Direct property access works: g.createdAt returns Date or null
// JSON.stringify(g) includes all fields

This is a separate observation from the null issue but may be related — get() returns an LMDB-backed object where no properties appear in Object.keys().

4. Scope

This is not limited to one table:

Table Schema Write method Records affected
Table A (@table) @createdTime + @updatedTime post() 1 of 25 records has createdAt: null
Table B (@table(expiration: 7776000)) @createdTime only post() All records have createdAt: null
Table C (@table) @createdTime + @updatedTime put() Some records have both timestamps null
Table D (@table(expiration: 7776000)) @createdTime + @updatedTime put() 1 record has createdAt: null

Expected behavior

@createdTime should populate createdAt with a Date timestamp on every post() and first put(), regardless of when the write occurs during the application lifecycle.

Environment

  • Harper Version: 4.7.24
  • Running on Harper Fabric
  • Schemas defined via .graphql files
  • TypeScript resources with Node.js type stripping
  • Records written programmatically via tables.TableName.post() and tables.TableName.put()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions