we-promise/sure · error · ActiveRecord::RecordNotFound
record_not_found
record_not_found
Error message
The requested resource was not found
What it means
Raised by Import::Preflight#preflight_account (app/models/import/preflight.rb:175) and rendered as HTTP 404 {error: 'record_not_found'} by Api::V1::BaseController#handle_not_found. It fires when the account_id sent to an import preflight request is not a UUID at all, or when it is a UUID that does not belong to an account in the authenticated family, because family.accounts.find is family-scoped. The valid_uuid? guard runs first so Postgres never receives a malformed uuid and throws its own 'invalid input syntax for type uuid' error.
Source
Thrown at app/models/import/preflight.rb:175
filename: filename,
content_type: content_type,
content: content,
parsed_rows_count: parsed_rows_count,
csv_headers: csv_headers,
missing_required_headers: missing_required_headers,
errors: errors,
warnings: warnings
)
}
)
end
def import_config_params
params.slice(*CONFIG_PARAM_KEYS)
end
def preflight_account
raise ActiveRecord::RecordNotFound unless Api::V1::BaseController.valid_uuid?(params[:account_id])
family.accounts.find(params[:account_id])
end
def csv_upload_attributes
if params[:file].present?
csv_file_upload_attributes(params[:file])
elsif params[:raw_file_content].present?
csv_raw_content_attributes(params[:raw_file_content].to_s)
end
end
def csv_file_upload_attributes(file)
raise_response csv_file_too_large_response if file.size > Import.max_csv_size
raise_response invalid_csv_file_type_response unless Import::ALLOWED_CSV_MIME_TYPES.include?(file.content_type)
[
file.read,View on GitHub (pinned to e69894adb9)
Solutions
- Fetch GET /api/v1/accounts with the same API key and use the id field of an account in that family
- Omit account_id entirely when the preflight does not need account scoping
- Strip whitespace/brackets and validate UUID format client-side before sending
- If the account was deleted, recreate it or pick another account before importing
Example fix
# before curl -H 'X-Api-Key: k' 'https://app/api/v1/imports/preflight?account_id=ACC-1042' # → 404 record_not_found # after ID=$(curl -s -H 'X-Api-Key: k' https://app/api/v1/accounts | jq -r '.data[0].id') curl -H 'X-Api-Key: k' "https://app/api/v1/imports/preflight?account_id=$ID"
Defensive patterns
Strategy: validation
Validate before calling
UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i
account_id = account_id.to_s.strip.gsub(/[{}]/, '')
raise ArgumentError, 'account_id must be a UUID' unless account_id.match?(UUID_RE)
ids = api.get('/api/v1/accounts').body['data'].map { |a| a['id'] }
raise ArgumentError, 'account_id not in this family' unless ids.include?(account_id) Type guard
def valid_uuid?(value)
value.is_a?(String) && value.match?(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i)
end Prevention
- Always source account_id from GET /api/v1/accounts under the same API key
- Never send UI slugs or human account numbers as account_id
- Omit account_id when the preflight does not need account scoping
When it happens
Trigger: Calling the imports preflight endpoint with params[:account_id] blank; a slug or account number like 'ACC-1042'; a bracketed/URL-encoded UUID such as %7B...%7D; or a valid UUID of an account owned by a different family or already deleted.
Common situations: Copy-pasting an account number from the web UI instead of the UUID from GET /api/v1/accounts; using an API key issued for a different family or self-hosted instance; a stale account_id cached client-side after the account was deleted; HTTP clients that URL-encode braces around UUIDs.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/de1e2f1a7d0393bb.
Report an issue: GitHub.